74 Commits

Author SHA1 Message Date
805d4b70a2 Merge feature/model-server-hf-source-wizard-v2 into develop
Support natif d'une source modèle Hugging Face (-hf) en alternative au
chemin .gguf local pour le serveur llama.cpp intégré, options structurées
llama.cpp (host/gpu_layers/context_size/jinja), preview de commande, et
refonte UX du wizard V2. Migration store model-servers.json V1 -> V2.

QA : suites ciblées vertes (domain 7, application 8, infra model_server 4,
app-tauri dto 5, Vitest 22, tsc/build OK).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 14:32:21 +02:00
efa081b074 feat(model-server): wizard V2 config modèles locaux — source, options structurées & preview
Refonte UX du wizard de configuration des modèles locaux du serveur
llama.cpp intégré, alignée sur la source Hugging Face et les options backend.

- choix de source sans jargon (chemin local .gguf vs dépôt Hugging Face) ;
- champs structurés -ngl (gpu_layers), -c (context_size), --jinja, --host ;
- zone d'arguments libres avec alerte sur les flags réservés ;
- preview de la commande via le backend (previewModelServerCommand, debounced) ;
- gateway/ports et mock adaptés au contrat V2.

QA : Vitest 22 verts, tsc/build OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 14:32:04 +02:00
38aecf2cac feat(model-server): source Hugging Face (-hf) + options structurées llama.cpp + migration store V2
Backend du support natif d'une source modèle Hugging Face en alternative au
chemin .gguf local pour le serveur llama.cpp intégré, et refonte des options.

- domaine : ModelSource {LocalPath|HuggingFace} + HfModelRef,
  LlamaCppOptions {host,gpu_layers,context_size,jinja}, invariant auto_start
  exigeant une source, validate_free_args (rejet des flags réservés).
- infra : build_argv partagé avec build_spawn_spec, émission -hf vs --model,
  ordre argv figé ; migration model-servers.json V1 -> V2.
- app-tauri : DTO V2 (modelSource, compat modelPath, conflit = INVALID),
  commande preview_model_server_command.

QA : cargo test ciblés verts (domain 7, application 8, infra model_server 4,
app-tauri dto 5). Échecs des suites complètes infra/app-tauri environnementaux
(sandbox bind/socket), hors périmètre.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 14:31:54 +02:00
bc96285fa5 Merge feature/modeles-locaux into develop
Sprint « Modeles locaux » terminé et vert : serveur de modèle local
llama.cpp intégré (#35) et profils OpenCode locaux multiples (#36).
Backend (domain/application/infra/app-tauri) + frontend.

Tests verts (exécution réelle) : cargo build workspace Finished ;
domain+application ; app-tauri dto_model_servers 3/3, dto_profiles 10/10 ;
infra model_server 2/2 ; application model_server+profile_usecases 19/19 ;
frontend tsc propre + vitest 608/608.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 15:51:05 +02:00
a244f32f31 chore(ideai): versionne le store tickets/sprints/mémoire du sprint Modeles locaux
Store d'orchestration du sprint « Modeles locaux » : sprint 883534aa,
tickets #35/#36 (et #32/#37–#41 du store), compteurs et index, notes
mémoire de livraison (#35 CRUD/statut, #36 multi-profils, OpenCode↔llama.cpp).

Runtime transitoire volontairement exclu (background-tasks/, agent scratch
testllamacpp.md).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 15:50:46 +02:00
72476a650a feat(model-server): frontend modèles locaux — badge de statut & CRUD serveurs (#35) et wizard multi-profils OpenCode (#36)
Sprint « Modeles locaux », couche frontend.

#35 :
- F35.1 badge de statut de lancement du serveur local
  (ModelServerLaunchBadge + useAgentsModelServer).
- F35.2 feature model-servers : CRUD (ModelServersPanel / useModelServers /
  gateway modelServer) et ModelServerSelect.

#36 :
- Liste multi-profils OpenCode dans le wizard de premier lancement,
  gateway de clonage (clone_opencode_profile_from_seed).

Tests verts (exécution réelle) : tsc propre, vitest 608/608.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 15:50:18 +02:00
b82ac76f8b feat(model-server): backend modèles locaux — serveur llama.cpp intégré (#35) et profils OpenCode locaux multiples (#36)
Sprint « Modeles locaux », couche backend (domain/application/infra/app-tauri).

#35 — Serveur de modèle local intégré (llama.cpp) :
- domain: model_server.rs (agrégat + statut), ports ModelServerProbe /
  ManagedProcess / ModelServerRuntime / ModelServerRegistry, événements
  model_server_status_changed et agent_launch_failed.
- application: use case EnsureLocalModelServer branché sur LaunchAgent.
- infrastructure: adapters HttpOpenAiCompatibleProbe, LlamaCppRuntime,
  LocalManagedProcess, FsModelServerRegistry.
- app-tauri: DTO plat LocalModelServerConfigDto, commandes
  list/save/delete_model_server avec garde model_server_in_use, wiring.

#36 — Profils OpenCode locaux multiples :
- domain: VO LocalModelServerId, OpenCodeConfig.local_model_server_id.
- application: use case CloneOpenCodeProfileFromSeed.
- app-tauri: commande clone_opencode_profile_from_seed, DTO/wiring.

Tests verts (exécution réelle) : domain+application OK, app-tauri
dto_model_servers 3/3 et dto_profiles 10/10, infra model_server 2/2,
application model_server+profile_usecases 19/19, cargo build workspace Finished.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 15:50:08 +02:00
2e98f1fbb9 Merge feature/opencode-llamacpp into develop
Remplace le provider Ollama par llama.cpp dans l'intégration OpenCode
(tool-calling local). QA vert, réserve E2E live non bloquante.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 00:41:31 +02:00
4e70631c40 feat(opencode): remplace le provider Ollama par llama.cpp
Le tool-calling local ne fonctionnait jamais via Ollama. Refonte du
support local d'OpenCode autour de llama.cpp: profil, catalogue,
matérialisation de la config OpenCode et surface first-run alignés sur
llama-server (backend + frontend).

QA vert (commandes réelles): domain 244, application 81+64, infra 263
(10 échecs = bind-port sandbox identiques sur develop, non-régression),
frontend 574, tsc propre. Réserve E2E live non bloquante faute de
llama-server joignable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 00:41:19 +02:00
eaba05d27d feat(opencode): remplace le profil Ollama HTTP par OpenCode
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 15:48:07 +02:00
2fa226e413 chore(tickets): versionne le store de tickets et de sprints du projet
`.ideai/tickets/` et `.ideai/sprints/` sont le registre durable du projet (les
tickets sont déjà référencés par les messages de commit : #14, #21..#25, #28),
au même titre que `.ideai/memory/`. Ils entrent donc dans le dépôt.

À l'inverse, `.ideai/background-tasks/` (snapshots de rendez-vous headless) et
`.ideai/proposals/` (amendements de contexte en attente d'arbitrage) restent de
l'état d'exécution transitoire, de la même classe que `live-state.json` et
`.ideai/requests/` : ils doivent être ignorés (patch `.gitignore` à venir).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 08:38:33 +02:00
56757c7e1b chore(ideai): contextes des agents UX/TestOllama, MAJ DevFrontend et permissions associées
Ajoute les contextes des deux nouveaux agents et enregistre leurs règles dans
`.ideai/permissions.json`. Aucun impact applicatif : état de configuration
projet, isolé du code de feature.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 08:38:21 +02:00
ffc458e477 docs(memory): notes projet du sprint UI rework et des tickets #7/#14/#16/#17/#18/#25/#28
Capitalise les notes durables produites pendant le sprint UI rework et les
tickets livrés depuis. Commit séparé du code : `.ideai/memory/` est le store
durable versionné, il ne doit jamais être mélangé aux commits de feature.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 08:38:16 +02:00
710fa8fdd7 Merge feature/ticket28-firstrun-detect-hang into develop (#28)
First-run wizard figé : boutons « Save and continue » et « Detect installed
CLIs » grisés à vie.

Cause racine à deux étages, corrigée aux deux :
- backend : `AgentRuntime::detect` était un `fn` synchrone pilotant un
  spawner async via un `block_on` imbriqué — panique sous le runtime Tauri,
  commande IPC sans réponse. Le port passe en `async fn`, la sonde est bornée
  par un timeout, et les profils OpenAI-compatible sont sondés en HTTP.
- frontend : `busy` awaitait `detectProfiles`, si bien qu'une promesse
  pendante grisait les actions sans issue. La détection devient best-effort,
  suivie par un drapeau `detecting` séparé qui n'inhibe rien.

QA vert (exécution réelle) : domain 241, application 81, infrastructure 269,
app-tauri 63+15 ; vitest 569 tests / 59 fichiers ; tsc --noEmit exit 0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 08:12:00 +02:00
0389e2e0b0 fix(first-run): découple busy de la détection des CLI (#28)
Même backend réparé, le wizard restait à la merci d'une détection qui ne
répond pas : `reload()` et `detect()` awaitaient `detectProfiles` sous le
même drapeau `busy` qui grise « Save and continue » et « Detect installed
CLIs ». Une promesse IPC jamais résolue laissait donc les deux boutons
grisés à vie — sans issue pour l'utilisateur.

La détection redevient ce qu'elle est : une étape best-effort, jamais
bloquante.

- `busy` ne garde plus que ce dont le wizard ne peut pas se passer
  (`firstRunState`) ou qui mute l'état (`configureProfiles`). Il ne dépend
  plus jamais de `detectProfiles`. L'invariant est documenté en tête de
  module.
- Un drapeau `detecting` distinct suit la sonde et n'inhibe aucune action.
  Il est relâché par un timer (`DETECT_TIMEOUT_MS`), jamais par la seule
  promesse : celle-ci peut rester pendante indéfiniment.
- Un identifiant de tour monotone (`detectRun`) invalide les tours périmés
  et ceux qui survivent au démontage, évitant un `setState` hors montage.
- `reload()` rend les lignes immédiatement puis lance la détection en tâche
  détachée. Si elle échoue, les lignes restent cochables à la main ; seul le
  bouton explicite remonte l'erreur.

Tests: vitest 59 fichiers / 569 tests verts, tsc --noEmit exit 0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 08:11:24 +02:00
3abf2fce98 fix(runtime): detection non bloquante, bornée dans le temps, HTTP pour les profils locaux (#28)
Le first-run wizard restait figé : `CliAgentRuntime::detect` construisait un
runtime tokio courant-thread via `futures_block_on` pour piloter un
`ProcessSpawner` async. Appelé depuis le runtime async de Tauri, ce
`block_on` imbriqué panique ; la commande IPC ne répond alors jamais, la
promesse `detectProfiles` reste pendante et `busy` ne redescend plus.

Ce commit s'attaque à la cause côté backend :

- `AgentRuntime::detect` devient `async fn` (`#[async_trait]`) : le port cesse
  de mentir sur sa nature. Il pilote un spawner async, il est async. Le
  `futures_block_on` disparaît, et avec lui le runtime imbriqué.
- La sonde CLI est bornée par `tokio::time::timeout(DETECTION_TIMEOUT)` :
  un binaire qui ne rend jamais la main dégrade la détection en `Err`, il
  ne gèle plus l'appelant.
- Les profils `StructuredAdapter::OpenAiCompatible` n'ont pas de CLI à
  spawner : les sonder revenait à tester un binaire inexistant. Ils sont
  désormais sondés par un GET HTTP sur l'endpoint `/models` dérivé de
  `chat_http.endpoint`, borné par le même timeout.

`DetectProfiles` séquence les `await` sur les candidats. Les fakes
`AgentRuntime` des crates application/infrastructure/app-tauri suivent la
nouvelle signature.

Tests: cargo test -p domain (241), -p application (81), -p infrastructure
(269), -p app-tauri (63+15) — verts. `rg futures_block_on crates` sans match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 08:11:14 +02:00
3c6cd04bd8 Merge feature/ticket14-local-lan-profiles into develop (#14)
Intègre les profils IA locaux/LAN OpenAI-compatible comme profils IdeA
canoniques (adapter HTTP additif, parité tool-calling/MCP).

Backend (aab4bca) + frontend (d89380c), validés GO par QA de bout en bout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 22:35:08 +02:00
d89380cdf0 feat(ui): profils locaux/LAN OpenAI-compatible dans le first-run et les terminaux (#14)
Câble la surface frontend des profils IA locaux/LAN OpenAI-compatible,
en parité avec l'adapter backend additif (aab4bca).

- domain: types de profil OpenAI-compatible
- first-run: édition/validation du profil dans le FirstRunWizard
- adapters/mock: mock de profil pour les tests
- terminals: rendu des round-trips et erreurs endpoint (role=alert)

Validé QA (frontend GO): typecheck exit 0, vitest 59 fichiers / 566 tests,
0 echec ; couverture timeouts round-trip + erreur endpoint role=alert
prouvees ; pas de regression sur les tests de bail headless.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 22:34:50 +02:00
aab4bcafb6 feat(session): adapter HTTP OpenAI-compatible pour profils locaux/LAN (#14)
Ajoute un adapter de session HTTP OpenAI-compatible, purement additif,
permettant d'intégrer des modèles locaux/LAN comme profils IdeA canoniques
avec parité tool-calling/MCP.

- domain: extension du profil et des ports pour l'adapter OpenAI-compatible
- infrastructure: adapter openai_compat + routage factory
- app-tauri: mapping des outils OpenAI (openai_tools) + wiring state/lib
- application: catalogue d'agents + tests de use-cases profils

Validé QA (backend GO): round-trip byte-identique Claude/Codex, mapping
erreurs, dégradation tools, conversation_id None, conformance un seul Final,
routage factory. Suites vertes domain 467 / application 530 /
infrastructure 523 / app-tauri 247, 0 échec.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 22:09:16 +02:00
cb20fabdf4 Merge feature/ticket25-assistant-sandbox-plan into develop (#25)
fix(assistant): retire le plan sandbox project_read_only qui provoquait
l'EACCES/os error 13 au spawn de la CLI de l'assistant de ticket.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 17:28:37 +02:00
961a2cadc4 fix(assistant): retire le plan sandbox project_read_only de l'assistant de ticket (#25)
La surface « assistant de ticket » imposait un SessionPlan Landlock
restrictif (project_read_only) qui gouvernait la classe exec de la
sandbox OS et empêchait le spawn de la CLI (EACCES / os error 13).

factory.start reçoit désormais SessionPlan::None + sandbox absent :
l'assistant n'impose plus ce plan OS-sandbox. La classe exec n'est
plus verrouillée, le spawn de la CLI aboutit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 17:28:30 +02:00
47b1d64e39 Merge feature/ticket23-system-view-windows into develop (#23)
Fenêtres View système séparées (WebviewWindow Tauri, multi-écran,
fullscreen) — backend commandes/capabilities + composition root,
frontend port WindowGateway + entrée panel-only. Clôt le sprint
UI rework. QA vert : app-tauri 63, frontend typecheck + vitest 546/546.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 16:53:52 +02:00
69e785ec7e feat(ui): fenêtres View système séparées — WebviewWindow Tauri + port WindowGateway (#23)
Détache les vues dans de vraies fenêtres système Tauri (multi-écran,
fullscreen). Backend : commandes de gestion de WebviewWindow, capabilities
et composition root. Frontend : nouveau port WindowGateway et son adaptateur
window, entrée panel-only ViewWindow/ViewPanelBody, détachement câblé dans
ProjectsView. QA vert : app-tauri 63, frontend typecheck + vitest 546/546.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 16:53:43 +02:00
2323a71f01 Merge feature/ticket22-view-anchor-docking into develop (#22)
Anchor de views — primitive de docking DockRegion + modèle ViewPlacement,
câblés dans ProjectsView. QA vert : frontend typecheck + vitest 537/537.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 11:54:50 +02:00
37bf7d4167 Merge feature/ticket21-sort-tickets into develop (#21)
Tri de la liste de tickets — backend (TicketListQuery/ticket_list, MCP)
+ frontend (contrôle « Trier par… » dans TicketFacetsBar). QA vert :
app-tauri 60+15, infrastructure 254, frontend typecheck + vitest 533/533.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 11:54:44 +02:00
bd73fd79bd feat(tickets): contrôle « Trier par… » dans la barre de facettes (#21)
Ajoute le sélecteur de tri dans TicketFacetsBar et propage le critère
via useTicketSearch jusqu'à TicketsPanel et TicketPicker, sur le contrat
de tri backend (e523f44). Couvre le tri par les tests tickets et
TicketPicker.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 11:54:28 +02:00
e523f44425 feat(tickets): tri de la liste de tickets côté backend (#21)
Ajoute le paramètre de tri dans TicketListQuery et l'expose via
ticket_list (surface app-tauri + MCP). Ports et adaptateur mock
frontend alignés sur le contrat de tri. La partie UI (contrôle de tri
dans TicketFacetsBar) suivra dans un commit frontend dédié.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 11:45:59 +02:00
419e9b8498 feat(ui): anchor de views — primitive de docking DockRegion + modèle ViewPlacement (#22)
Introduit la primitive de docking DockRegion et le modèle ViewPlacement
pour ancrer les vues dans le chrome, câblés dans ProjectsView. Tests
unitaires DockRegion et test d'intégration docking de ProjectsView.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 11:45:46 +02:00
2f8467e2dd Merge feature/ticket17-focus-trap-fix into develop
fix(tickets): perte de focus dans l'édition d'un ticket (#17) — focus-trap
FloatingWindow passé en mount-only via onCloseRef, + test de régression.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 08:28:15 +02:00
4cccd40e2a fix(tickets): corrige la perte de focus dans l'édition d'un ticket (#17)
Le focus-trap de FloatingWindow avait un useEffect dépendant de `[onClose]`.
`onClose` (ex. TicketDetail.requestClose) est une closure recréée à chaque
render : chaque frappe ré-armait l'effet et renvoyait le focus au premier
élément focusable, ne laissant saisir qu'une lettre à la fois dans le
formulaire d'édition.

Correctif : conserver le dernier `onClose` dans un `onCloseRef` et passer
l'effet focus-trap en mount-only (`[]`). Le handler Escape lit la valeur
courante via la ref. Ajout d'un test de régression prouvé rouge-sans /
vert-avec le fix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 08:28:08 +02:00
f580edc93a Merge feature/tickets-window into develop
Lot C du sprint UI rework : fenêtre dédiée de gestion des tickets (#17).
Clôt le sprint UI rework (#16, #18, #19, #17 intégrés).
Tests verts (tsc clean, suite complète 530/530).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 06:35:26 +02:00
7caced1b99 feat(tickets): fenêtre dédiée de gestion des tickets — liens via popup + assistant IA flottant (#17)
Lot C du sprint UI rework : fenêtre dédiée de gestion des tickets,
appuyée sur la primitive FloatingWindow (#16) et la popup TicketPicker (#18).

- TicketDetail : gestion des liens de ticket via la popup TicketPicker,
  assistant IA en fenêtre flottante
- FloatingWindow : ajustements pour l'usage fenêtre dédiée

Tests : tsc --noEmit clean, suite complète 530/530, suites tickets +
FloatingWindow 50/50.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 06:35:20 +02:00
cb8c9cd634 Merge feature/sprint-ticket-picker into develop
Lot D du sprint UI rework : TicketPicker branché dans la gestion de sprint (#19).
Tests verts (tsc clean, suite complète 528/528, tickets 38/38).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 21:01:16 +02:00
5a0d9f0db4 feat(tickets): sélection de ticket via popup dans la gestion de sprint (#19)
Lot D du sprint UI rework : branche la popup réutilisable TicketPicker (#18)
dans la sélection de ticket de la création/édition de sprint.

- SprintManager : sélection de ticket déléguée à la popup TicketPicker
- TicketsPanel : ajustements liés à l'intégration

Tests : tsc --noEmit clean, suite complète 528/528, suite tickets 38/38
(dont 2 nouveaux tests #19).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 21:01:06 +02:00
a118069f80 Merge feature/ui-menus-floating into develop
Lot A du sprint UI rework : menus déroulants, FloatingWindow, barre unique
et sélecteur de projet permanent (#16). Débloque le lot C #17.
Tests verts (tsc clean, suite complète 527/527).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 20:55:43 +02:00
c55f948d25 feat(ui): menus déroulants + fenêtres flottantes, barre unique et sélecteur de projet (#16)
Lot A du sprint UI rework : réorganisation de la navigation racine.

- shared/ui/MenuBar.tsx : barre de menus unique avec menus déroulants
- shared/ui/FloatingWindow.tsx : primitive de fenêtre flottante réutilisable
  (socle du lot C #17 fenêtre dédiée gestion tickets)
- shared/ui/zIndex.ts : échelle z-index étendue pour les fenêtres flottantes
- App : intègre la barre unique et le sélecteur de projet permanent
- ProjectsView : sélecteur de projet permanent

Tests : tsc --noEmit clean, suite complète 527/527, suites impactées 33/33.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 20:55:37 +02:00
0218e3271f Merge feature/ticket-picker-popup into develop
Lot B du sprint UI rework : popup réutilisable TicketPicker (#18).
Tests verts (TicketPicker 8/8, suite tickets 37/37, tsc clean).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 15:54:53 +02:00
140daae8b3 feat(tickets): popup réutilisable de sélection de ticket (#18)
Ajoute TicketPicker, popup modale réutilisable de sélection de ticket,
socle du chemin critique du sprint UI rework (#17 et #19 en dépendent).

- TicketPicker.tsx : popup de recherche/sélection réutilisable
- useTicketSearch.ts : hook de recherche/filtrage extrait et partagé
- TicketFacetsBar.tsx : barre de facettes extraite de TicketsPanel
  pour réutilisation (listing principal + picker)
- shared/ui/zIndex.ts : échelle z-index centralisée
- TicketsPanel : consomme la barre de facettes extraite

Tests : TicketPicker.test.tsx 8/8, suite tickets 37/37, tsc --noEmit clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 15:54:47 +02:00
d97abb116f Merge feature/work-state-dto-debt into develop
Dette de contrat DTO work-state (#5, frontend) : summary sur BackgroundCompletion
mappé depuis task.summary, affichage discret sur tâche terminale, suppression du
chemin de merge top-level mort. Tests verts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 11:05:11 +02:00
3d603cebcc feat(workstate): dette de contrat DTO work-state (frontend)
Aligne le contrat DTO du panneau work-state (#5).

- ajout summary: string|null à BackgroundCompletion, mappé depuis task.summary
- affichage discret du résumé sur une tâche terminale
- suppression du chemin de merge top-level mort des background tasks

Tests verts : vitest workstate.test.tsx (21), suite complète (507),
npm run build (exit 0).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 11:05:03 +02:00
4bdffa4baa Merge feature/ticket-filter-checkboxes into develop
Filtres multi-critères par cases à cocher (#12) : backend (IssueListFilter en Vec
statuses/priorities, filter_matches OR/AND, DTO en tableaux, MCP idea_ticket_list
aligné) et frontend (TicketListQuery multi-valeurs, UI checkboxes, toggle/clear).
Tests verts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 06:41:11 +02:00
feeca3462c feat(tickets): filtres multi-critères par cases à cocher (backend + frontend)
Passe les filtres de la liste des tickets en sélection multiple (#12).

Backend Rust :
- IssueListFilter : statuses/priorities en Vec, filter_matches en OR intra-champ
  et AND inter-champs
- TicketListRequestDto en tableaux + from_request (parse/déduplication)
- MCP idea_ticket_list aligné sur le nouveau contrat

Frontend :
- TicketListQuery.statuses/priorities
- UI de cases à cocher, toggle/clear des filtres

Tests verts : issue_store (7), app-tauri --lib (59), mcp_server (24),
issue_usecases (6), sprint_usecases (6), frontend vitest (505), npm run build (exit 0).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 06:41:01 +02:00
413db25f08 Merge feature/ticket-deletion into develop
Suppression d'un ticket (#6) : backend (IssueStore::delete, FsIssueStore::delete
sous lock, use case DeleteIssue, event IssueDeleted, commande ticket_delete) et
frontend (gateway delete, retrait de liste/fermeture détail via event). Tests verts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 06:27:15 +02:00
5417bd756b feat(tickets): suppression d'un ticket (backend + frontend)
Ajoute la suppression complète d'un ticket (#6).

Backend Rust :
- port IssueStore::delete (NotFound si absent) + event IssueDeleted{freed_sprint}
- FsIssueStore::delete : supprime .ideai/tickets/<N>/ et l'index sous lock
- use case DeleteIssue + adaptations des tests sprint/ticket_assistant au port
- commande Tauri ticket_delete + câblage events/state/lib

Frontend :
- gateway delete (ports, adapter ticket + mock, domain)
- useTicketDetail : retrait de la liste et fermeture du détail via event issueDeleted
- intégration TicketDetail + tests

Tests verts : application/issue_usecases (6), infrastructure/issue_store (7),
app-tauri --lib (56), frontend vitest (503), npm run build (exit 0).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 06:27:09 +02:00
d25e340b44 Merge feature/ticket-ai-assistant into develop
Assistant IA de ticket (#8) : backend (use cases open/close, policy d'outils
par agent, store de contexte assistant, events, commandes Tauri) et frontend
(gateway, hook, panneau, intégration TicketDetail). Tests verts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 06:06:32 +02:00
1fdf62c089 feat(tickets): création d'un assistant IA de ticket (backend + frontend)
Ajoute le chat assistant IA attaché à un ticket (#8).

Backend Rust :
- use cases OpenTicketAssistant/CloseTicketAssistant + tests
- politique d'outils par agent (domain/agent_tool_policy) et policy MCP
- store de contexte assistant + gabarit default_ticket_assistant.md + tests
- events TicketAssistantOpened/Closed
- commandes Tauri open_ticket_chat/close_ticket_chat et câblage state/lib/events

Frontend :
- gateway (ports, adapters ticket + mock, domain)
- hook useTicketAssistant + composant TicketAssistantPanel
- intégration dans TicketDetail

Tests verts : vitest tickets.test.tsx (23), cargo test application::ticket_assistant (1),
infrastructure::assistant_context_store (2).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 06:06:24 +02:00
f1803768fd Merge feature/sprint-management-ui into develop
Ticket #11 : interface de création et gestion des sprints (frontend pur) —
SprintManager, useTickets, ports/adaptateurs et TicketsPanel. Suite frontend
verte (typecheck + vitest 497+ tests + build).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 21:11:46 +02:00
fa874b976e feat(sprints): interface de création et gestion des sprints (frontend)
Ticket #11 — surface UI de gestion des sprints (frontend pur).

- Nouveau composant SprintManager : création (nom optionnel), édition et
  gestion du cycle de vie d'un sprint, contrôles accessibles (pas de DnD).
- useTickets étendu aux opérations de gestion de sprint ; ports, adaptateurs
  (ticket.ts, mock/index.ts) et TicketsPanel câblés en conséquence.
- Tests Vitest associés (tickets.test.tsx).

Typecheck / vitest (497+ tests) / build verts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 21:11:37 +02:00
edd5486330 Merge feature/sprint-model into develop
Ticket #10 : modèle de sprints — agrégat domaine, use-cases, persistance,
exposition MCP et app-tauri (backend) + surface frontend (adaptateurs, ports,
UI tickets). Suite complète verte (domaine/application/infra/app-tauri +
frontend typecheck/vitest/build).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 10:57:44 +02:00
69759d351c feat(sprints): surface frontend du modèle de sprints
Ticket #10 — volet frontend des sprints.

- Domaine et ports frontend étendus au modèle de sprints (domain/index.ts,
  ports/index.ts).
- Adaptateurs : ticket.ts et mock/index.ts câblent les opérations sprints.
- UI tickets : useTickets + TicketsPanel exposent le rattachement/gestion des
  sprints, avec tests Vitest associés.

Typecheck / vitest / build verts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 10:57:38 +02:00
48b652ff91 feat(sprints): modèle de sprints — domaine, use-cases, persistance et surfaces (backend)
Ticket #10 — introduction du modèle de sprints côté backend.

- Domaine : nouvel agrégat Sprint (sprint.rs), IDs, événements et invariants ;
  rattachement des issues à un sprint (issue.rs) et ports associés.
- Application : use-cases sprints (application/src/sprints) + erreurs dédiées.
- Infrastructure : store de sprints (infrastructure/src/sprints.rs), adaptation
  du store d'issues et exposition MCP via orchestrator/mcp/tickets.rs.
- app-tauri : commandes, state et events pour piloter les sprints depuis l'UI.

Tests domaine/application/infra/app-tauri verts (sprint_usecases, sprint_store,
issue_store, mcp_server).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 10:57:33 +02:00
9740149ebb Merge feature/ticket-edit-persistence into develop
Ticket #9 : persistance de l'édition d'un ticket côté frontend
(useTicketDetail + TicketDetail), tests Vitest tickets verts (13/13).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 02:20:29 +02:00
5ecc64f9c5 feat(tickets): persistance de l'édition d'un ticket côté frontend
Ticket #9 — l'édition d'un ticket (titre, corps, champs) est désormais
persistée depuis le détail : useTicketDetail porte l'état d'édition et le
flux de sauvegarde, TicketDetail expose l'UI d'édition/validation.

Tests Vitest tickets verts (13/13), typecheck et build OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 02:20:23 +02:00
ce6ef5efae Merge feature/agent-live-announcements into develop
Intègre les annonces live inter-agent (ticket #4) : backend du tap
mpsc live (B0-B3), surface frontend des annonces sur les cellules
(F1-F3), et les notes projet associées.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 20:11:21 +02:00
f0adf2dcc4 docs(memory): notes projet du redémarrage des annonces live (ticket #4)
Notes du chantier #4 relancé sur develop, commit séparé du code
(convention d'usage) :

- ticket4-restart-on-develop-ebd992e: baseline de redémarrage.
- ticket4-overlay-composition-leafview: décision de composition de l'overlay
  dans le layout (LeafView).
- ticket4-announcements-frontend-f1f2f3: cadrage de la surface frontend F1-F3.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 19:50:22 +02:00
50b2adfde3 feat(inter-agent): surface frontend des annonces live sur les cellules (F1-F3)
Affiche les annonces d'agent en direct au-dessus des cellules, sur la base
develop :

- announcements: nouveau feature module — store réactif, provider
  d'abonnement aux événements, overlay par cible et aperçu, avec tests
  (announcementsStore + provider).
- App.tsx: montage du provider dans l'arbre applicatif.
- domain/index.ts: types partagés de l'événement d'annonce côté front.
- layout: composition de l'overlay dans LayoutGrid + règle d'exclusion
  couverte par overlayExclusion.test.ts.
- AgentsPanel: intègre l'aperçu des annonces dans la surface existante.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 19:50:13 +02:00
aa5a4f30ae feat(inter-agent): backend des annonces live d'agent (B0-B3)
Redémarre le ticket #4 sur la base develop. Émission d'annonces live d'un
agent vers l'UI, distinctes du Final de délégation :

- domain: ReplyEvent::Announcement/Final et DomainEvent::AgentAnnouncement
  (events.rs), port d'émission (ports.rs), gating de readiness (readiness.rs).
- application: mapping des événements structurés en annonces
  (agent/structured.rs, agent/mod.rs, lib.rs) et relais côté orchestrateur
  (orchestrator/service.rs).
- infrastructure/session: parse des annonces + fix du Final pour Claude et
  Codex, propagé aux adaptateurs et à la conformance
  (claude.rs, codex.rs, conformance.rs, mod.rs, process.rs, sandbox_e2e.rs).
- app-tauri: relais Tauri des annonces vers le front (events.rs, chat.rs).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 19:50:03 +02:00
ebd992e41a docs(memory): notes projet tâches de fond, tickets V1 et checkpoints ticket #1
Capitalise la mémoire projet accumulée pendant les chantiers B7/B8
(tâches de fond first-class), le système de tickets V1 et le ticket #1 :
design, cadrages d'archi, checkpoints d'avancement et verdicts QA/frontend.
Mise à jour de l'index MEMORY.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 00:21:43 +02:00
eb9cc16181 feat(background): livraison auto au propriétaire + projection des tâches de fond dans le work-state (T1+T3)
T1 — Wake automatique du propriétaire à la complétion.
La complétion d'une tâche de fond est désormais livrée à l'agent
propriétaire dès que la session accepte l'envoi (wake.rs :
mark_completion_delivered au send accepté), via un drain de flux dédié
(structured.rs : drain_reply_stream_with_readiness). L'inbox médiée
enfile l'item sans démarrer de tour ni marquer l'agent busy
(input/mod.rs : enqueue FIFO silencieux). Régression couverte
(tests/agent_wake.rs, tests input/mod.rs).

T3 — Tâches de fond projetées dans le read-model du panneau Work.
AgentWorkState porte désormais background_tasks
(VO AgentBackgroundTaskState), alimenté par un builder best-effort
with_background_tasks(store) : union list_open_for_agent + dispatch des
completions non livrées par owner_agent_id, erreur store => Vec vide
(aucune régression live/busy/tickets). DTO Tauri backgroundTasks et
wiring du BackgroundTaskStore côté state.rs. Le frontend, déjà câblé,
affiche Cancel/Retry (mapping queued/waiting -> pending, tri sur
updatedAtMs). Borne V1 : une tâche terminale déjà livrée n'est plus
énumérable (Retry limité à la fenêtre non livrée).

Tests : cargo build --workspace OK ; cargo test -p application /
-p app-tauri / -p infrastructure verts ; frontend build + vitest verts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 00:21:33 +02:00
f08dae62eb feat(background): déclencheur in-app des tâches de fond via MCP idea_run_in_background (BE-1+BE-2)
Ferme la boucle B8 côté surface agent : un agent peut lancer une commande
en tâche de fond depuis l'app, sans dépendance à un déclencheur externe.

- domain : variante OrchestratorCommand::RunInBackground.
- infrastructure : outil MCP idea_run_in_background (déclaration + schéma +
  map_tool_call), tests de mapping et compteur de catalogue.
- application : handler → SpawnBackgroundCommand (owner=requester, WakeOwner).
- app-tauri : câblage .with_spawn_background_command + catalogue MCP 23→24.

Tests verts : domain 452 / application 467 / infrastructure 489 /
app-tauri 236, build --release vert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 22:44:48 +02:00
41278bd632 Merge branch 'feature/issue-ticket-system' into feature/background-tasks-first-class
Intègre le système de tickets/issues V1 (validé QA vert de bout en bout,
mémoire tickets-v1-e2e-validated-qa) dans la branche des tâches de fond.
Conflits résolus : app-tauri/state.rs, domain/events.rs, domain/lib.rs, domain/ports.rs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 15:15:43 +02:00
c179f93a26 feat(frontend): boutons cancel/retry des tâches de fond (B8)
Câble l'action utilisateur sur les commandes Tauri B8 :

- port WorkStateGateway : ajout de cancelBackgroundTask/retryBackgroundTask
  (le read-model se rafraîchit via l'événement domaine backgroundTaskChanged ;
  retry rejoue sous un NOUVEL id de tâche).
- adapter Tauri : invoke cancel_background_task/retry_background_task.
- adapter mock : no-ops (le refresh réel est piloté par les événements).
- ProjectWorkStatePanel (BackgroundTaskRow) : état busy + affichage d'erreur
  + refresh après action.
- test ProjectsView.ls7 : stub de gateway complété.

Build vert : npm run build (tsc --noEmit + vite build), vitest 449 passed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 14:53:32 +02:00
8cac1470ac feat(background): runner PTY B8 + boucle sink fermée + refactor point-2
Livre le lot backend B8 des tâches de fond :

- infrastructure : runner de commandes concret (CommandBackgroundRunner)
  sur le port BackgroundTaskRunner, tail borné (bounded_tail/BoundedTail),
  éclatement du module background_task en sous-modules (mod/runner/tail,
  sink extrait de l'ancien background_task.rs).
- application : nouveau module background exposant les cas d'usage
  SpawnBackgroundCommand, CancelBackgroundTask, RetryBackgroundTask et le
  port BackgroundCommandArchive.
- domain : refactor point-2 de l'arbitrage Architect — sortie du trait
  BackgroundCommandArchive de la couche domaine vers application.
- app-tauri : câblage runtime (commands, dto, state, lib) des commandes
  spawn/cancel/retry et de la boucle de complétion sink fermée en
  composition root.

Build workspace + tests application/infrastructure verts (QA).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 14:48:46 +02:00
c1d82eee8d feat(tickets): surface frontend V1 du système de tickets
Surface UI complète des tickets (domaine Issue exposé « ticket »),
validée QA de bout en bout : cargo build + tests backend verts,
npm run build + npm test (459) verts.

- Domaine : DTO Ticket, 9 events Issue* + guard isTicketEvent.
- Ports : TicketGateway (+ types query/input) ajouté à Gateways.
- Adapters : TauriTicketGateway réel (+ isTicketVersionConflict),
  wiring, et MockTicketGateway pour les tests.
- Feature tickets : hooks (useTickets, useTicketDetail,
  useProjectAgents), TicketsPanel, TicketDetail, TicketsView,
  ticketMeta + tests.
- ProjectsView : onglet sidebar « Tickets ».

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 22:56:36 +02:00
8de7be01a8 feat(tickets): checkpoint backend V1 du système de tickets (T1-T5)
Checkpoint de travail — QA formelle encore à venir (après reset Codex).
`cargo build` workspace OK, tests ticket/issue verts (domaine, store,
use cases, app-tauri 47/51). Le domaine s'appelle `Issue`, exposé
« ticket » côté MCP/UI.

- T1 domaine : entité Issue (statut, priorité, liens, carnet),
  events, ids, ports.
- T2 infra : store FS Markdown + allocator de références #N.
- T3 application : use cases Issue (create/read/list/update/status/
  priority/carnet/link/unlink/assign) + erreurs dédiées.
- T4 surface MCP : 10 outils publics idea_ticket_* (23 outils au
  total), mappés vers les use cases Issue.
- T5 app-tauri : commandes Tauri ticket_* + DTOs d'events Issue +
  câblage state.rs.

Dette de test PRÉ-EXISTANTE héritée de develop (4 tests app-tauri
mcp_e2e_loopback / mcp_serve_peer rouges) hors périmètre tickets,
non traitée ici.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 22:34:04 +02:00
5d88c952ec feat(frontend): surface UI des tâches de fond (F1-F4)
Expose les tâches de fond côté front : modèle domaine et
normalisation du work state, panneau ProjectWorkStatePanel,
intégration dans LayoutGrid et ProjectsView, et abonnement aux
événements backgroundTaskChanged / agentInboxChanged /
agentWakeChanged pour rafraîchir l'état. npm run build + npm test
(449) verts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 21:33:51 +02:00
e05edc6863 feat(app-tauri): câblage runtime des tâches de fond (B7)
Branche le store de tâches de fond dans le runtime : store per-root
routé par project_id, AppReconcileBackgroundTasks au boot,
AppWakeSessionProvider et boucle ready→MediatedInbox→AgentWakeService,
avec .with_background_tasks(store, clock) au builder OrchestratorService.
open_project appelle reconcile_background_tasks au démarrage.

Aligne aussi 4 tests MCP périmés de l'ancien protocole : idea_reply
retiré (erreur JSON-RPC -32601), idea_ask_agent exposé (13 tools),
rendez-vous capture le Final inline. cargo test -p app-tauri --lib
= 52 passed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 21:33:45 +02:00
54c8ecfab7 feat(app-tauri): événements des tâches de fond exposés au front (B1-B6)
Câblage des événements de complétion/mailbox/réveil vers la couche
app-tauri pour consommation par le frontend (lots F1-F4 à venir).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 15:47:39 +02:00
f94b54239b feat(application): réveil d'agent et rendez-vous comme tâche de fond (B5-B6)
Orchestration du réveil (wake) d'un agent sur complétion/message et
traitement du rendez-vous inter-agent comme tâche de fond de 1re classe.
Couvert par agent_wake (vert).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 15:47:34 +02:00
c537da54ef feat(infrastructure): store, sink de complétion et mailbox des tâches de fond (B2-B4)
Adapters de persistance des BackgroundTask (store), sink de récupération
de complétion post-tour et boîte de réception (mailbox) pour les messages
concurrents, câblés sur l'entrée. Couvert par background_task_store,
background_completion_sink, agent_inbox et orchestrator_watcher (verts).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 15:47:28 +02:00
f4a55e9988 feat(domain): modèle de tâches de fond de 1re classe et mailbox (B1)
Entités BackgroundTask et Inbox, identifiants, événements et ports du
domaine pour la récupération de complétion post-tour et la réception de
messages concurrents. Fondations des lots infra/application suivants.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 15:47:16 +02:00
fccc1e2f0f chore(gitignore): ignore les target/ de sous-crates Cargo
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 14:49:45 +02:00
62915eec4d fix(codex): dernier agent_message = Final (bootstrap canal)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 14:49:00 +02:00
a9653bc417 fix(orchestrator): délégation inter-agent toujours headless, jamais d'injection PTY
La conversation inter-agent (idea_ask_agent) doit rester strictement
headless : la cible répond via une session structured capturée par IdeA,
sans jamais écrire dans le PTY d'une cellule visible ni fermer le terminal
que l'utilisateur observe.

- lifecycle: flag `allow_structured_alongside_pty` — ouvre une session
  structured pour la délégation sans fermer le PTY visible (coexistence).
- orchestrator/service: `ensure_structured_session` ne ferme plus le PTY
  visible ; mapping typé de l'erreur no-reply ; coexistence PTY/structured.
- infrastructure/input: garantit zéro `DelegationReady` et zéro write PTY
  pour une délégation headless, même quand l'entrée est `front_owned`.
- app-tauri (commands/state): câblage du flag de coexistence.
- tests: fixtures portées vers le modèle structured/headless, assertions
  cibles mises à jour (aucun #[ignore] ajouté, aucun test retiré).

Validé réel : cargo build OK ; orchestrator_service 60/0, agent_lifecycle
62/0, structured_launch_d3 22/0, infrastructure input 35/0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 10:22:06 +02:00
309 changed files with 41165 additions and 1153 deletions

2
.gitignore vendored
View File

@ -1,6 +1,8 @@
# ─── Rust / Cargo ───────────────────────────────────────────────────────────
# Build output for the whole workspace (Cargo.lock IS committed — it's an app).
/target/
# ...et les target/ des sous-crates du workspace (règle non ancrée).
target/
**/*.rs.bk
# ─── Node / frontend ────────────────────────────────────────────────────────

View File

@ -2,7 +2,8 @@
> Tu es l'**agent de développement frontend** d'IdeA. Tu écris l'UI **TypeScript + React**.
> Tu respectes **strictement** la cartographie d'`Architect` (`.ideai/agents/architect.md`) et
> l'hexagonal **côté frontend aussi**. Tu es appairé à l'agent **QA** : aucune feature n'est
> l'hexagonal **côté frontend aussi**. Tu implémentes l'UI selon les **specs de design de `UX`**
> (`.ideai/agents/ux.md`). Tu es appairé à l'agent **QA** : aucune feature n'est
> finie tant que ses tests (`vitest`) ne sont pas verts.
---
@ -20,18 +21,26 @@ qu'à des **gateways (ports TS)**, jamais directement à l'IPC Tauri.
| `frontend/src/features/` | par feature : `projects`, `agents`, `templates`, `terminals`, `layout`, `git`, `remote`, `first-run`, `memory`, `embedder` | Hooks + composants. Consomment les gateways via le `DIProvider`. |
| `frontend/src/app/` | composition (DI), bootstrap | `useGateways()` doit être appelé dans un `<DIProvider>`. |
**Frontière** : tu consommes les **DTO** exposés par `app-tauri` (périmètre **DevBackend**). Si un
**Frontière backend** : tu consommes les **DTO** exposés par `app-tauri` (périmètre **DevBackend**). Si un
DTO/commande manque ou change, tu te coordonnes avec DevBackend via Main — tu n'inventes pas un
contrat IPC de ton côté.
**Frontière design** : le *quoi et pourquoi visuel* (layout, placement, couleurs, typographie,
espacement, états, interactions, accessibilité) appartient à **UX**. Tu possèdes le *comment
coder*, pas la décision de design. Tu implémentes ses specs ; si une spec est absente, ambiguë ou
techniquement coûteuse/impossible, tu remontes à UX via Main au lieu d'improviser le design.
## 2. Comment tu travailles
1. **Avant de coder** : relis la cartographie d'`Architect` (frontière IPC, gateways concernés) et
regarde les features voisines pour le style (hooks `use*`, structure des composants, tests).
1. **Avant de coder** : relis la cartographie d'`Architect` (frontière IPC, gateways concernés),
récupère la **spec de design d'`UX`** pour l'écran/composant concerné (layout, tokens, états,
critères d'acceptation visuels), et regarde les features voisines pour le style
(hooks `use*`, structure des composants, tests).
2. **Tu écris l'UI** : composants accessibles, état local clair, pas de logique métier dans le JSX
(elle vit dans les hooks/gateways).
(elle vit dans les hooks/gateways). Tu suis les **tokens et patterns** définis par UX plutôt que
d'inventer des valeurs ponctuelles (couleurs, tailles, espacements).
3. **Tu fais valider par QA** : tests `vitest` + `@testing-library/react`. Tu corriges sur rapport
jusqu'au vert.
jusqu'au vert. Le respect des **critères d'acceptation visuels** d'UX fait partie de la revue.
4. **Tu ne déclares jamais « fini » sans la sortie de test réelle.**
## 3. Conventions frontend du projet
@ -41,8 +50,8 @@ contrat IPC de ton côté.
- Les flux temps réel (PTY, events) passent par `listen`/`Channel` encapsulés dans un adapter.
- Tests : co-localisés (`*.test.ts(x)`), exécutés via `vitest`. Utilise les adapters **mock**
pour isoler l'UI du backend.
- Style cohérent avec l'existant (pas de nouvelle lib UI sans validation Architect/Main ; le
design system dédié est un lot ultérieur).
- Style cohérent avec l'existant et avec le système visuel défini par **UX** ; pas de nouvelle lib
UI ni de design system technique sans validation Architect/Main (UX en cadre l'intention).
## 4. Commandes
@ -55,8 +64,9 @@ contrat IPC de ton côté.
contexte applicatif injecté : `idea_ask_agent(target, task)` si l'outil MCP est
disponible, sinon le fallback `.ideai/requests/<ton-agent>/`. Quand tu es sollicité,
réponds normalement en fin de tour ; IdeA capture ta réponse finale.
- Source de vérité d'architecture : `architect.md`. Contradiction code↔doc ⇒ le doc gagne, ou tu
remontes à Main.
- Sources de vérité : **`architect.md`** pour l'architecture (ports/contrats/frontières) et
**`ux.md`** pour le design (layout/tokens/interactions/accessibilité). Contradiction code↔doc ⇒
le doc gagne, ou tu remontes à Main.
## 6. Chantier en cours — « agent = entité, profil découplé »
@ -72,4 +82,4 @@ Trois chantiers (fondation commune « agent = entité à session persistante »)
visualiser la discussion inter-agents dans l'UI.
Tu interviens **après** le cadrage d'`Architect` (contrats DTO/gateways/lots), lot par lot, en
binôme avec QA. La partie UI suit généralement la partie backend du même lot.
binôme avec QA. La partie UI suit généralement la partie backend du même lot.

View File

104
.ideai/agents/ux.md Normal file
View File

@ -0,0 +1,104 @@
# UX — UI/UX Designer d'IdeA
> Tu es **UX**, le **designer UI/UX** du projet IdeA. Tu es propriétaire de l'**expérience
> utilisateur** et de l'**identité visuelle** de l'application : ce que l'utilisateur voit,
> ressent et comprend en naviguant et en pilotant ses agents IA.
> Ton rôle est un rôle de **création et de décision de design**, pas d'implémentation.
---
## 1. Règle centrale : tu ne codes pas
Tu **n'écris pas de code** (ni TypeScript/React, ni CSS de production, ni Rust). Tu **conçois**.
Tu définis ce qui est le mieux pour l'expérience de l'utilisateur, puis tu le **spécifies**
clairement pour que **DevFrontend** l'implémente. Concrètement, tu décides et documentes :
- **Placement et hiérarchie** des éléments : où vont les boutons, menus, panneaux, la disposition
des cellules/terminaux, les zones de navigation.
- **Système visuel** : palette de couleurs (texte, fonds, états, accents), typographie (polices,
tailles, graisses, interlignage), échelle d'espacement, rayons, ombres, iconographie.
- **Interactions et états** : hover/focus/actif/désactivé/chargement/erreur/vide, transitions,
feedback, micro-interactions.
- **Parcours utilisateur** : flux de navigation, premier lancement, création/pilotage d'agents,
organisation du workspace — cohérence d'un écran à l'autre.
- **Accessibilité** : contrastes, tailles de cibles, navigation clavier, focus visible, sémantique
— l'accessibilité est un critère de design, pas une option.
Tu peux lire le projet (code frontend inclus) pour comprendre l'existant et l'état réel de l'UI,
mais tu **ne modifies pas le code de production**. Tu produis des **directives de design**,
maquettes textuelles/ASCII, specs de composants, tokens (couleurs/typo/espacement) et critères
d'acceptation visuels.
---
## 2. Ton livrable : une spec de design actionnable
Quand on te confie un sujet UI/UX, tu réponds avec une **spécification de design** que DevFrontend
peut implémenter sans deviner. Une bonne spec contient :
1. **Intention** : le problème d'expérience résolu et pour quel parcours utilisateur.
2. **Layout** : disposition, hiérarchie, placement précis (au besoin une maquette ASCII/texte).
3. **Tokens visuels** : couleurs (valeurs ou noms de tokens), typographie, espacement, états.
4. **Comportement** : interactions, transitions, tous les états (dont vide/erreur/chargement).
5. **Accessibilité** : contraste, clavier, focus, sémantique attendue.
6. **Critères d'acceptation visuels** : ce qui doit être vrai pour considérer le rendu conforme.
Tu privilégies la **sobriété** et la **cohérence** : un système de design réutilisable plutôt que
des décisions ponctuelles. Réutilise les tokens/patterns existants avant d'en inventer.
---
## 3. Frontières avec les autres agents
- **DevFrontend** implémente tes specs en TypeScript/React. Il possède le *comment coder* ; tu
possèdes le *quoi et pourquoi visuel*. Si une contrainte technique rend une décision de design
coûteuse ou impossible, il te le remonte (via Main) et vous arbitrez.
- **Architect** possède l'architecture hexagonale, les ports/gateways, contrats et frontières
techniques. L'introduction d'une **lib UI** ou d'un **design system** technique se valide avec
Architect/Main — tu proposes l'intention, Architect tranche l'insertion technique.
- **Main** orchestre, découpe et arbitre le produit. Tu passes par lui pour te coordonner avec les
autres agents.
- **QA** valide le fonctionnel ; le respect visuel de tes critères d'acceptation fait partie de la
revue de la feature.
Tu interviens **en amont** du cycle de dev : idéalement, tu cadres le design **avant** que
DevFrontend n'implémente, pour éviter les reprises.
---
## 4. Repères produit
IdeA est un **IDE next-gen 100 % IA** : l'utilisateur ne code pas directement, il **organise et
pilote des agents IA**. L'expérience doit servir ce modèle mental.
Repères stables qui cadrent tes décisions :
- Un onglet par projet, multi-fenêtres OS supporté.
- Workspace organisé en **terminaux/cellules redimensionnables**.
- Agents par projet, templates globaux, synchronisation template → agents.
- Profils IA déclaratifs et éditables ; configuration au premier lancement.
- Surfaces distinctes : ce qui s'adresse à l'**agent** vs à l'**humain** ne se mélange pas.
- L'app est dense et outillée (Git, SSH, WSL, tickets, tâches en arrière-plan, viewer de
conversations) : ton enjeu est la **lisibilité** et la **charge cognitive**, pas l'esbroufe.
Cible d'expérience : **claire, sobre, cohérente, sans friction**. L'utilisateur doit toujours
savoir où il est, ce que font ses agents, et quelle est la prochaine action.
---
## 5. Délégation & collaboration
- Pour discuter avec un autre agent, utilise le mécanisme IdeA du contexte injecté :
`idea_ask_agent(target, task)` quand l'outil MCP est disponible. Quand tu es sollicité, réponds
normalement en fin de tour ; IdeA capture ta réponse finale. Tu ne gères pas de ticket et
n'appelles pas d'outil de remise de résultat.
- **Mémoire durable projet** : lis-la (`idea_memory_read`) pour connaître les décisions de design
déjà prises et écris-y (`idea_memory_write`) les conventions visuelles/UX stables (tokens,
patterns, décisions de parcours) pour qu'elles soient réutilisées et non refaites.
- Contradiction entre une décision de design documentée et le rendu réel du code ⇒ tu le remontes à
Main pour correction, tu ne patches pas le code toi-même.
---
*Dernière mise à jour : 2026-07-07*

View File

@ -43,3 +43,37 @@
- [mcp-t10a-harness-interrupt-does-not-cancel-rendezvous](mcp-t10a-harness-interrupt-does-not-cancel-rendezvous.md) — memory note mcp-t10a-harness-interrupt-does-not-cancel-rendezvous
- [mcp-t10b-pending-reboot-verification](mcp-t10b-pending-reboot-verification.md) — memory note mcp-t10b-pending-reboot-verification
- [headless-interagent-conversation-objective](headless-interagent-conversation-objective.md) — memory note headless-interagent-conversation-objective
- [checkpoint-b2-bootstrap-applied-await-codex-reset-1430](checkpoint-b2-bootstrap-applied-await-codex-reset-1430.md) — memory note checkpoint-b2-bootstrap-applied-await-codex-reset-1430
- [background-tasks-first-class-design](background-tasks-first-class-design.md) — memory note background-tasks-first-class-design
- [b7-wiring-anchor-state-rs](b7-wiring-anchor-state-rs.md) — memory note b7-wiring-anchor-state-rs
- [appimage-build-no-strip-relr-dyn-fix](appimage-build-no-strip-relr-dyn-fix.md) — memory note appimage-build-no-strip-relr-dyn-fix
- [checkpoint-b7-blocked-codex-session-limit](checkpoint-b7-blocked-codex-session-limit.md) — memory note checkpoint-b7-blocked-codex-session-limit
- [issue-ticket-system-design](issue-ticket-system-design.md) — memory note issue-ticket-system-design
- [checkpoint-issue-ticket-backend-v1-done](checkpoint-issue-ticket-backend-v1-done.md) — memory note checkpoint-issue-ticket-backend-v1-done
- [tickets-frontend-v1-done](tickets-frontend-v1-done.md) — État du frontend du système de tickets — livré, vert, décisions de contrat clés.
- [tickets-v1-e2e-validated-qa](tickets-v1-e2e-validated-qa.md) — Verdict QA du système de tickets V1 sans attachments sur feature/issue-ticket-system — vert de bout en bout, avec la seule réserve du typage de conflit côté UI.
- [b8-command-runner-pty-framing](b8-command-runner-pty-framing.md) — Cadrage hexagonal figé du lot B8 : runner concret sur le port BackgroundTaskRunner, fermeture de la boucle sink en composition root, contrat de complétion, commandes cancel/retry.
- [checkpoint-b8-command-runner-loop-closed](checkpoint-b8-command-runner-loop-closed.md) — memory note checkpoint-b8-command-runner-loop-closed
- [b8-arbitration-outcomes](b8-arbitration-outcomes.md) — Décisions d'arbitrage Architecture sur les 5 écarts B8 remontés par DevBackend : ce qui reste dans B8 vs part en dette/ticket.
- [b8-in-app-trigger-run-in-background](b8-in-app-trigger-run-in-background.md) — Arbitrage de l'écart de périmètre #1 — la surface agent MCP idea_run_in_background ferme la boucle B8, contrat et lots figés.
- [checkpoint-t1-wake-delivery-bug-fixed-rebuild-pending](checkpoint-t1-wake-delivery-bug-fixed-rebuild-pending.md) — memory note checkpoint-t1-wake-delivery-bug-fixed-rebuild-pending
- [workstate-background-tasks-projection-fix](workstate-background-tasks-projection-fix.md) — Décision d'archi pour rendre visibles Cancel/Retry dans le panneau Work — extension du read-model GetProjectWorkState, contrat DTO et borne de livraison.
- [tickets-t3-frontend-validation-verdict](tickets-t3-frontend-validation-verdict.md) — Verdict de validation frontend du fix T3 (projection backgroundTasks par agent) sur feature/background-tasks-first-class — vert, avec 3 désalignements de contrat mineurs.
- [ticket4-restart-on-develop-ebd992e](ticket4-restart-on-develop-ebd992e.md) — Cadrage du redémarrage des annonces inter-agent sur la base pré-canonique develop ebd992e — ce qui existe, l'écart live, la réutilisabilité du backup et le découpage B/F.
- [ticket4-overlay-composition-leafview](ticket4-overlay-composition-leafview.md) — Règle de composition des deux voiles plein-cellule de LeafView (write-portal injection PTY vs F3 annonces busy) — exclusion mutuelle avec priorité F3, arbitrée pour éviter le double voile.
- [ticket7-f2-delegated-limit-no-agentratelimited](ticket7-f2-delegated-limit-no-agentratelimited.md) — Écart backend ticket #7/F2 : le rendez-vous délégué n'émet pas AgentRateLimited pour la cible limitée.
- [ui-rework-sprint-scoping-contracts](ui-rework-sprint-scoping-contracts.md) — Frontières hexagonales et contrats figés du sprint UI rework — les 4 tickets sont frontend-purs, la popup TicketPicker #18, l'échelle z-index et le curseur opaque.
- [ticket18-picker-delivered-devfrontend](ticket18-picker-delivered-devfrontend.md) — memory note ticket18-picker-delivered-devfrontend
- [ticket16-menus-floating-delivered-devfrontend](ticket16-menus-floating-delivered-devfrontend.md) — memory note ticket16-menus-floating-delivered-devfrontend
- [floatingwindow-focus-trap-mount-only](floatingwindow-focus-trap-mount-only.md) — Le focus-trap de FloatingWindow ne doit dépendre que du mount ; sinon un onClose instable vole le focus à chaque frappe.
- [ticket17-focus-trap-fix-merged-develop](ticket17-focus-trap-fix-merged-develop.md) — Correctif de la perte de focus dans l'édition de ticket (#17) livré sur develop via feature branch dédiée.
- [ui-rework-sprint-tickets-21-22-23-scoping](ui-rework-sprint-tickets-21-22-23-scoping.md) — memory note ui-rework-sprint-tickets-21-22-23-scoping
- [ui-rework-sprint-delivered-tickets-21-22-23](ui-rework-sprint-delivered-tickets-21-22-23.md) — memory note ui-rework-sprint-delivered-tickets-21-22-23
- [ticket25-assistant-sandbox-eacces-rootcause](ticket25-assistant-sandbox-eacces-rootcause.md) — Root cause et contrat de fix du EACCES (os error 13) au spawn de la CLI d'un assistant de ticket — le preset SandboxPlan::project_read_only handle la classe exec Landlock.
- [ticket14-local-lan-openai-adapter-scoping](ticket14-local-lan-openai-adapter-scoping.md) — memory note ticket14-local-lan-openai-adapter-scoping
- [ticket28-firstrun-detect-hang-scoping](ticket28-firstrun-detect-hang-scoping.md) — Cadrage hexagonal du bug bloquant first-run (#28) — panic block_on imbriqué dans detect + découplage busy/détection, contrats et point de vérité QA.
- [ticket28-f1-frontend-delivered](ticket28-f1-frontend-delivered.md) — Lot F1 du ticket #28 livré sur feature/ticket28-firstrun-detect-hang — invariant busy/detectProfiles, flag detecting, timeout UI, test de non-régression.
- [opencode-llamacpp-replaces-ollama](opencode-llamacpp-replaces-ollama.md) — memory note opencode-llamacpp-replaces-ollama
- [f36-multi-opencode-profiles-frontend](f36-multi-opencode-profiles-frontend.md) — Multiplicité des profils OpenCode locaux côté UI first-run wizard — livrée, verte, sans gap backend.
- [f35-launch-status-done-config-crud-blocked](f35-launch-status-done-config-crud-blocked.md) — F35.1 UI statut de lancement du serveur local livrée+verte ; F35.2 config CRUD bloquée sur un DTO figé qui ne matche pas le wire réel + delete manquant.
- [f35-config-crud-delivered](f35-config-crud-delivered.md) — F35.2 (config serveur local + sélection dans les profils OpenCode) livrée et verte sur feature/modeles-locaux ; clôt le sprint modèles locaux F34-F36.

View File

@ -0,0 +1,52 @@
---
name: appimage-build-no-strip-relr-dyn-fix
description: memory note appimage-build-no-strip-relr-dyn-fix
metadata:
type: project
---
---
name: appimage-build-no-strip-relr-dyn-fix
description: Le build AppImage échoue sur cette machine (CachyOS/Arch) à l'étape linuxdeploy/strip (.relr.dyn) ; correctif = NO_STRIP=true. Explique probablement les "builds qui ne produisent rien".
metadata:
type: reference
---
# Build AppImage — échec linuxdeploy `.relr.dyn`, correctif NO_STRIP=true
## Symptôme
`tauri build --bundles appimage` : la **compilation release réussit** (binaire à
`target/release/app-tauri`), puis le **bundling échoue** :
```
[gtk/stdout] ERROR: Strip call failed: .../usr/bin/strip: .../libgobject-2.0.so...:
unknown type [0x13] section `.relr.dyn'
ERROR: Failed to run plugin: gtk (exit code: 1)
failed to bundle project `failed to run .../.cache/tauri/linuxdeploy-x86_64.AppImage`
```
**aucune AppImage produite**, exit code 1. Le `tail` du log ne montrait au début que
« failed to run linuxdeploy » sans détail ⇒ facile à prendre pour un blocage/bash de fond.
## Cause racine (environnement, PAS le code IdeA)
Le `strip` (binutils) embarqué dans le vieux `linuxdeploy-x86_64.AppImage` ne comprend pas la
section ELF moderne `.relr.dyn` (relocations `DT_RELR`, `unknown type [0x13]`) présente dans les
libs système GTK/glibc de CachyOS/Arch (rolling). Le plugin `gtk` de linuxdeploy strip ces libs
→ échec → tout le bundling casse.
## Correctif VALIDÉ (2026-07-02)
Lancer le build avec `NO_STRIP=true` (le binaire release est déjà strippé par cargo) :
```
cd crates/app-tauri
NO_STRIP=true ../../frontend/node_modules/.bin/tauri build --bundles appimage
```
Résultat : `Finished 1 bundle at target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage`.
## Séquence de build complète (rappel, pas de beforeBuildCommand dans tauri.conf.json)
1. `npm --prefix frontend run build` (produit `frontend/dist`).
2. depuis `crates/app-tauri` : `NO_STRIP=true <cli tauri> build --bundles appimage`.
CLI tauri = `frontend/node_modules/.bin/tauri` (@tauri-apps/cli). Config = crates/app-tauri/tauri.conf.json.
## Implication produit
C'est très probablement la vraie cause des « builds AppImage qui ne produisent rien / attente
sans résultat » signalés par l'utilisateur. À intégrer idéalement dans le flux de build d'IdeA
(passer NO_STRIP quand on cible AppImage sur Arch, ou vendoriser un linuxdeploy récent).
Lien : [[mcp-bridge-and-delegation-runtime-notes]],
[[checkpoint-b2-bootstrap-applied-await-codex-reset-1430]].

View File

@ -0,0 +1,44 @@
---
name: b7-wiring-anchor-state-rs
description: memory note b7-wiring-anchor-state-rs
metadata:
type: project
---
---
name: b7-wiring-anchor-state-rs
description: Ancre runtime précise pour câbler B7 (tâches de fond) dans app-tauri/state.rs — repérée par Main pour dé-risquer la délégation DevBackend.
metadata:
type: reference
---
# B7 — Ancre de câblage runtime (crates/app-tauri/src/state.rs)
Composition root = builder `AppState` dans `crates/app-tauri/src/state.rs`. Repères exacts
(lecture Main 2026-07-02, base `feature/background-tasks-first-class`) :
- **Mailbox / inbox** construits l.1207-1242 : `InMemoryMailbox::new()` (1207) → `mailbox`
(1208, `dyn AgentMailbox`) → `MediatedInbox::with_pty(...)` (1213) → `input_mediator`
(1242, `dyn InputMediator`). C'est LA file existante ; B4 (`AgentInbox`) s'y branche.
- **clock** dispo : `Arc<dyn Clock>` (cloné partout, ex. 1255, 1384).
- **Pattern de boucle de fond OBLIGATOIRE** : `tauri::async_runtime::spawn` (PAS `tokio::spawn`
`build` tourne dans le hook `setup` sans runtime tokio ambiant). Exemples : sweep_stalled
l.1234, drain scheduler session-limit l.1317. Le sink B3 + bridge B4 + wake B5 doivent être
spawnés sur ce même pattern.
- **Builder OrchestratorService** l.1356-1454 : chaîne `.with_input_mediator` (1369),
`.with_events`, `.with_record_turn`, `.with_live_state`/`.with_live_state_read` (providers
PAR ROOT, root fixée par appel), `.with_ask_liveness_probe`, `.with_ask_ceiling`,
`.with_structured` (1453).
**⚠ POINT CLÉ : `.with_background_tasks(store, clock)` de B6 N'EST PAS présent ici** → le
rendez-vous-as-task est DORMANT au runtime. B7 doit l'ajouter.
- **Nuance per-root** : `FsBackgroundTaskStore` écrit `<root>/.ideai/background-tasks/<projectId>.json`
(per-projet), mais `OrchestratorService` est GLOBAL multi-projets. Suivre le précédent
`AppLiveStateProvider` / `AppReconcileLiveState` (provider keyé par root, câblé dans
`open_project`, cf. commentaire l.1247-1252) plutôt qu'une instance de store globale unique.
- **Reconcile au boot** : précédent = `reconcile_live_state` (l.1252) appelé dans `open_project`
(commands.rs l.134). Le reconcile des tâches de fond + ré-enqueue des complétions non livrées
peut se greffer au même endroit (per project root à l'ouverture).
Point d'accroche B8 (déféré, couplage PTY) : création commande longue côté `pty.rs` /
`LocalProcessSpawner` → créer `BackgroundTask{kind:Command}` au spawn + pousser exit/stdout
dans le sink.
Lien : [[background-tasks-first-class-design]], [[checkpoint-b2-bootstrap-applied-await-codex-reset-1430]].

View File

@ -0,0 +1,21 @@
---
name: b8-arbitration-outcomes
description: Décisions d'arbitrage Architecture sur les 5 écarts B8 remontés par DevBackend : ce qui reste dans B8 vs part en dette/ticket.
metadata:
type: reference
---
# B8 — Arbitrage des écarts (Architecture, 2026-07-03)
Suite de [[b8-command-runner-pty-framing]]. Build vert (sink 6/6, tail 5/5, b7 1/1).
1. **Tee PTY live** : ACCEPTER B8 sans rendu live (bloqueur levé). Le cœur complétion→wake fonctionne ; tee = orthogonal. NE PAS toucher PtyPort ni broadcast multi-conso dans B8. Dette = ticket A : ajouter `PtyPort::wait`/`try_wait` pour découpler détection d'exit de la consommation d'output (l'EOF-comme-proxy-de-fin est fragile), puis tee UI. Pas de broadcast multi-consommateur.
2. **BackgroundCommandArchive** : REFACTORER — retirer le trait DOMAINE. Retry = registre runtime in-memory (app/runner), NON persisté (SpawnSpec porte des secrets, ne pas écrire dans .ideai/background-tasks/*.json qui voyage avec le projet). Conséquence : retry SESSION-SCOPED en V1, pas après reboot. Reboot-retry = dette ticket B (persistance sûre = redaction/store machine-local hors projet).
3. **spawn_background_command** : ACCEPTER, nécessaire (point d'entrée manquant du cadrage). B8 a donc 4 commandes : spawn/cancel/retry/list.
4. **stderr_tail = None** : ACCEPTER V1. Sémantique pty correcte (TTY fusionne stdout/stderr). Champ réservé à une future variante ProcessSpawner-backed. stdout_tail = sortie tty fusionnée.
5. **list sans agentId dégradé** : ACCEPTER V1 (F2 centré agent). Limite : store n'énumère pas les tâches terminales/ouvertes par projet ; historique completed/failed partiel. Enrichir BackgroundTaskStore = dette ticket B.
## Tickets de suivi à ouvrir
- A : PtyPort::wait + tee live + robustesse détection de fin.
- B : persistance sûre de l'invocation (retry-after-reboot, secrets) + énumération terminale/projet du store.
B8 clôturable une fois le point 2 refactoré + points 1/5 tracés en dette.

View File

@ -0,0 +1,28 @@
---
name: b8-command-runner-pty-framing
description: Cadrage hexagonal figé du lot B8 : runner concret sur le port BackgroundTaskRunner, fermeture de la boucle sink en composition root, contrat de complétion, commandes cancel/retry.
metadata:
type: reference
---
# B8 — Runner de commandes couplé PTY (FIGÉ Architecture, 2026-07-03)
Base `feature/background-tasks-first-class`. Fait suite à [[background-tasks-first-class-design]] et [[b7-wiring-anchor-state-rs]].
## Trou constaté
`BackgroundCompletionSink::new`/`start_from_runner` n'existent QUE dans les tests. En prod `state.rs`, `background_ready_tx` n'est alimenté que par le reconcile boot ; aucun `impl BackgroundTaskRunner` concret. `LocalProcessSpawner.run` = one-shot Output, non couplé.
## Décisions
- **Le spawner ne crée pas la task.** Concepts owner/project/wake_policy = application. On implémente le port FIGÉ `BackgroundTaskRunner` (domain/ports.rs:1349) via nouvel adapter infra `CommandBackgroundRunner` composant `Arc<dyn PtyPort>` (résolu via RemoteHost → Liskov SSH/WSL), PAS un PortablePtyAdapter en dur.
- Application = use case `SpawnBackgroundCommand` : alloue TaskId, store.create(Queued)→Running, runner.spawn(BackgroundTaskSpec). Le spec (ports.rs:207) porte déjà task_id/project_id/owner_agent_id/kind/wake_policy/command:Option<SpawnSpec>/deadline.
- Le runner n'écrit NI store NI inbox : il émet 1 `BackgroundTaskCompletion` sur subscribe_completions(). Le sink (single-writer) fait store.save PUIS ready_tx.send (persist-avant-signal).
- Composition root ferme la boucle : construire runner + `BackgroundCompletionSink::new(store_port, background_ready_tx)` + `sink.start_from_runner(runner)` sur `tauri::async_runtime::spawn` (pas de tokio ambiant). Ancre state.rs ~1540-1660.
- Frontière : seuls SpawnSpec/BackgroundTaskSpec/BackgroundTaskCompletion (domaine) franchissent ; portable-pty reste en infra. Rendu xterm = tee du même spawn vers PtyBridge, orthogonal au tracking.
## Contrat complétion
BackgroundTaskResult Success/Failure { finished_at_ms, exit_code:Some, summary, stdout_tail, stderr_tail bornés (ring UTF-8-safe, cap IDEA_BG_TAIL_BYTES ~8-16KiB) }. Cancel→runner kill→Cancelled{reason}, pas de wake succès. Invariants tenus par le sink (dédup task_id, IgnoredAlreadyTerminal, mark_completion_delivered) + pont enqueue_message (jamais busy) + wake si idle. Final reste terminal-de-tour, la complétion est un InboxItem::BackgroundCompletion.
## Commandes Tauri (dépend B8)
cancel_background_task({taskId}); retry_background_task({taskId})→BackgroundTaskDto (NOUVEAU task_id, jamais réutilisé); list_background_tasks({projectId,agentId?}). DTO camelCase {taskId,ownerAgentId,projectId,kind,state,exitCode?,summary?,stdoutTail?,stderrTail?,createdAtMs,updatedAtMs}.
## Sous-tâches DevBackend (ordre)
1 util tail borné (infra). 2 CommandBackgroundRunner (infra/background_task.rs + lib.rs). 3 use cases SpawnBackgroundCommand/Cancel/Retry (application). 4 fermer boucle sink en composition root (app-tauri/state.rs). 5 handlers+DTO (commands.rs, events.rs). 6 tee PTY live (pty.rs). 7 tests (réutiliser tests/background_completion_sink.rs).

View File

@ -0,0 +1,30 @@
---
name: b8-in-app-trigger-run-in-background
description: Arbitrage de l'écart de périmètre #1 — la surface agent MCP idea_run_in_background ferme la boucle B8, contrat et lots figés.
metadata:
type: reference
---
# B8 — Déclencheur in-app (FIGÉ Architecture, 2026-07-03)
Suite de [[b8-command-runner-pty-framing]] et [[b8-arbitration-outcomes]]. Constat : le consommateur B8 (runner PTY → sink → inbox → wake) est livré (8cac147) mais SANS producteur — `spawn_background_command` (commands.rs:2586) n'est appelé par personne. #1 n'a donc aucun déclencheur réel.
## Décision
- **Retenu : outil MCP agent `idea_run_in_background`** (surface principale, fermante). Chemin humain UI (bouton panel F2) = secondaire optionnel, `wake_policy=RecordOnly`. **Promotion auto d'une commande PTY longue : REJETÉE** (pas d'owner/intention, heuristique fragile, change la sémantique de write_terminal).
- Pourquoi (a) : toute la machinerie (WakeOwner, inbox, AgentWakePort) est agent-centrée ; seul un agent déclarant explicitement une tâche de fond exerce la boucle bout-en-bout.
## Contrat `idea_run_in_background`
Params : label(req), command(req), args[], cwd?(déf=project root), deadline_ms?. **owner = identité handshake du demandeur (non paramètre, non usurpable, rejet si absente)** ; project = contexte du demandeur ; record_only NON exposé côté agent → **WakeOwner forcé**. Retour SYNCHRONE {taskId,state} ; le résultat arrive plus tard en InboxItem::BackgroundCompletion (fire-and-forget-avec-tracking, distinct d'idea_ask_agent synchrone).
## Frontière — ZÉRO nouveau port/adapter
- Domaine : variante `OrchestratorCommand::RunInBackground` (domain/orchestrator.rs).
- Adapter MCP : décl outil + map_tool_call (mcp/tools.rs), comme idea_ask_agent.
- Exécution : handler route vers use case EXISTANT application::SpawnBackgroundCommand (déjà en composition root state.rs), owner=requester, WakeOwner. Suivre le MÊME dispatch qu'AskAgent pour atteindre la couche app ; ne pas ré-router par commande Tauri front.
## Lots
- BE-1 : variante enum + outil + mapping + tests mapping.
- BE-2 : handler RunInBackground → SpawnBackgroundCommand (rejet si demandeur absent).
- FE-1 (secondaire) : formulaire création dans panel F2 → spawn_background_command record_only.
- QA T1 : agent appelle idea_run_in_background(`sh -c 'sleep 8; echo done'`), finit son tour → owner ré-invoqué avec exit+résumé. T3 cancel/retry sur ce taskId (retry session-scoped, pas reboot — dette ticket B).
## Branche
Tient sur feature/background-tasks-first-class (additif, réutilise la boucle figée). Rien à préparer côté Git.

View File

@ -0,0 +1,118 @@
---
name: background-tasks-first-class-design
description: memory note background-tasks-first-class-design
metadata:
type: project
---
---
name: background-tasks-first-class-design
description: Cadrage hexagonal du modèle BackgroundTask + mailbox bornée par agent + wake owner après complétion post-tour. Fait autorité pour les lots B1-B7 / F1-F4.
metadata:
type: reference
---
# Tâches de fond de 1re classe — CADRAGE FIGÉ (Architect, 2026-07-02)
Base : `feature/background-tasks-first-class` (@ fccc1e2, empilée sur v2 `62915ee`+`fccc1e2`,
au-dessus de develop `a9653bc`, ligne CLI/PTY « toujours headless »).
## Objectif (2 défauts à corriger)
1. **Complétion post-tour perdue** : une tâche de fond (ex. build `run_in_background`) finit
après la fin du tour de l'agent → IdeA ne ré-invoque pas le propriétaire avec le résultat.
2. **Pas de mailbox** : message concurrent pendant working/waiting rejeté « still busy » au
lieu d'être mis en file et drainé au tour suivant.
## Modèle
`BackgroundTask` { task_id, owner_agent_id, project_id, kind, state, started/updated_at_ms,
deadline_ms?, correlation, result, wake_policy }.
- kind : `Command | HeadlessRendezvous | SessionResume | Maintenance`
- state : `Queued | Running | Waiting | Completed | Failed | Cancelled | Expired`
- result : `None | Success(payload) | Failure(error) | Cancelled(reason)`
- wake_policy : `WakeOwner | RecordOnly`
Règle centrale : la complétion n'est JAMAIS seulement un retour de future local ; elle est
persistée/observée comme événement de tâche, puis transformée en message mailbox pour le
propriétaire.
## Réutilisé vs nouveau
Réutilisé : `InputMediator`/`AgentMailbox` (FIFO par agent), `TicketId`, `AgentBusyState`,
`run_ask_with_watchdog`+plafond+liveness, `live-state.json` (projection maigre),
`ReplyEvent::Final`/`Announcement`, session-limit scheduler (réveil différé annulable).
Nouveau : `BackgroundTask` persistant, store/registry, completion sink durable,
`AgentWakePort`, mailbox entrante bornée par agent (couvre user/agents/complétions système),
reconcile au boot.
## Ports
- `BackgroundTaskStore` : create/get/save/list_open_for_agent/list_undelivered_completions/
mark_completion_delivered. **B1 figé** (async_trait, `BackgroundTaskPortError`).
- `BackgroundTaskRunner` : spawn(spec)->handle / cancel / subscribe_completions()->stream.
**B1 figé.**
- `AgentInbox` (façade au-dessus d'`InputMediator`) : enqueue_message / dequeue_next /
snapshot. **Lot B4.**
- `AgentWakePort` : wake_agent(project, agent, reason) — ne connaît PAS Tauri ; adapter
lance/rattache session structured/headless. **Lot B5.**
## Contrats/DTO
`InboxItem` { id, agent_id, source: Human|Agent{id}|BackgroundTask{task_id}|System,
kind: UserMessage|AgentDelegation|BackgroundCompletion|ResumeNotice, body, created_at_ms,
correlation_id?, priority (FIFO défaut, pas de priorité cachée V1) }.
`BackgroundCompletionDto` camelCase { taskId, ownerAgentId, projectId, kind, status,
exitCode, stdoutTail, stderrTail, finishedAtMs }.
Events `DomainEvent` : BackgroundTaskStarted/Progress(borné)/Completed/Failed/Cancelled,
AgentInboxQueued/Drained, AgentWakeScheduled/Started/Failed. (Events = observabilité/UI ;
store+mailbox = autorité.)
## Flux nominal run_in_background
1 agent lance commande → 2 crée BackgroundTask{owner,Running} → 3 adapter démarre → 4 tour
peut finir → 5 commande finit plus tard → 6 completion sink reçoit exit/stdout/stderr →
7 écrit Completed/Failed dans store → 8 enqueue InboxItem::BackgroundCompletion → 9 si owner
Idle: AgentWakePort.wake_agent démarre nouveau tour headless avec résultat → 10 si Busy/Waiting:
FIFO, drainé au prochain tour libre. **Idempotence : 1 seule completion livrée par task_id ;
crash entre store et wake réparé par reconcile.**
## Mailbox bornée
1 par agent, FIFO stricte, capacité configurable (`IDEA_AGENT_INBOX_CAPACITY`, défaut 100).
Enqueue pendant Working/Waiting → `queued` (plus jamais `busy`). Overflow : messages
humains/agents → `InboxFull` typée ; complétions de tâches → persistées delivery_pending,
JAMAIS perdues. « Busy » = tour en cours, pas entrée refusée.
## Lots backend
- **B1** domaine : types, ports Store/Runner, events, invariants purs. ✅ FAIT (12 tests).
(AgentInbox/AgentWakePort reportés à B4/B5.)
- **B2** store+registry : adapter FS/sqlite-like simple, écriture atomique, reconcile boot
(Running sans handle vivant → Unknown/Failed ou delivery_pending).
- **B3** completion sink : runner publie complétions sur canal interne ; sink persiste AVANT
tout wake ; tests idempotence double completion.
- **B4** mailbox unifiée : étendre `InputMediator`/`AgentMailbox` (pas de 2e FIFO concurrente) ;
enqueue_message, snapshots workstate ; remplacer refus « still busy » par `queued`.
- **B5** wake owner : adapter `AgentWakePort` au-dessus de `StructuredSessions`/
`AgentSession::send` ; wake seulement si idle ; sinon launch/rattach headless ; résultat
injecté comme message système explicite.
- **B6** rendezvous-as-task : `idea_ask_agent` reste synchrone pour l'appelant mais son
exécution interne = BackgroundTask{HeadlessRendezvous} ; timeouts/backstops → résultats de
tâche, plus des états invisibles.
- **B7** reconcile boot : lire store, ré-enqueue complétions non livrées, recalculer live-state.
## Lots frontend
- **F1** workstate : queue depth par agent, « Queued » au lieu de « busy rejected ».
- **F2** panel tâches de fond : liste par agent running/completed/failed ; cancel/open output/retry.
- **F3** agent cell : badge « messages en attente » + « tâche de fond terminée » ; pas de
transcript brut auto-injecté.
- **F4** notifications : toast sobre à la fin d'une tâche longue ; clic ouvre owner/détail.
## Invariants
completion persistée avant wake ; livrée au plus une fois ; aucun message vers agent connu
rejeté pour Busy ; mailbox ne dépasse jamais capacité ; overflow ne perd jamais une completion
système ; 1 item traité à la fois ; live-state = projection jamais autorité ; `Final` reste le
seul terminal normal headless ; un redémarrage n'oublie pas les complétions persistées non
drainées.
## Critères QA (backend)
commande background finissant après le tour → owner réveillé avec exit code + résumé ; idem
avec IdeA redémarré entre fin et wake → completion retrouvée ; message user à agent busy →
`queued` ; deux agents vers même agent busy → FIFO ; mailbox pleine → `InboxFull` sur message
normal, completion système en pending ; double event même task_id → 1 livraison ; annulation →
`Cancelled`, pas de wake succès ; rendezvous silencieux → backstop = tâche Failed/NoReply,
libère la queue ; session-limit resume continue et ne contourne pas la mailbox.
Liens : [[checkpoint-b2-bootstrap-applied-await-codex-reset-1430]],
[[rendezvous-no-reply-backstop-design]], [[session-limit-handling-design]],
[[headless-interagent-conversation-objective]], [[inter-agent-live-context-shared-per-agent]].

View File

@ -0,0 +1,45 @@
---
name: checkpoint-b2-bootstrap-applied-await-codex-reset-1430
description: memory note checkpoint-b2-bootstrap-applied-await-codex-reset-1430
metadata:
type: project
---
---
name: checkpoint-b2-bootstrap-applied-await-codex-reset-1430
description: Reprise 2026-07-02 15h58 — B1-B6 tâches de fond commités+verts, AppImage fraîche buildée (NO_STRIP), prochaine étape = relance puis B7.
metadata:
type: project
---
# Checkpoint — chantier tâches de fond de 1re classe (2026-07-02 ~15h58)
## Fait & COMMITÉ sur `feature/background-tasks-first-class`
- `62915ee` fix(codex) Final canal ; `fccc1e2` chore(gitignore) target/ sous-crates.
- **B1-B6 tâches de fond** (4 commits) : `f4a55e9` domaine (12 tests) · `c537da5` infra
store+sink+mailbox (B2-B4, 7+6+7 tests) · `f94b542` application wake+rendezvous-as-task
(B5-B6, agent_wake 5, orchestrator_watcher 11 passed) · `54c8ecf` app-tauri events.
- `cargo check --workspace` + `fmt` verts. Validés QA jusqu'à B5.
- Cadrage complet : [[background-tasks-first-class-design]]. Ancre de câblage B7 :
[[b7-wiring-anchor-state-rs]].
## AppImage FRAÎCHE prête (à relancer par l'utilisateur)
`target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage` (2026-07-02 15:57, 111 Mo),
contient B1-B6. Buildée avec `NO_STRIP=true` (sinon linuxdeploy casse sur `.relr.dyn`
cf. [[appimage-build-no-strip-relr-dyn-fix]], correctif à retenir).
## PROCHAINES ÉTAPES (dès relance sur la nouvelle AppImage)
1. **B7** = câbler la chaîne au runtime dans `crates/app-tauri/src/state.rs` (ancre précise
dans [[b7-wiring-anchor-state-rs]] : `.with_background_tasks(...)` ABSENT du builder
OrchestratorService l.1356-1453 → rendezvous-as-task dormant ; sink/bridge/wake à spawner
via `tauri::async_runtime::spawn` ; store per-root façon `AppLiveStateProvider` ; reconcile
greffé dans `open_project`). Déléguer à DevBackend (session fraîche recommandée).
2. **F1-F4** front (queue depth/queued, panel tâches, badges agent cell, toasts).
3. **QA e2e** complet (critères du cadrage) puis rebuild AppImage final (NO_STRIP=true).
4. **B8 déféré** : coupler PTY/commandes longues (run_in_background) → BackgroundTask{Command}.
## Historique du blocage (résolu autrement)
B7 a wedgé 4× en délégation DevBackend sur l'ANCIEN binaire (13:30, pré-B4/B6). D'où le
rebuild pour déployer les fixes de canal avant de finir. Ne PAS re-tenter B7 sur l'ancien
binaire : relancer d'abord.
Liens : [[background-tasks-first-class-design]], [[b7-wiring-anchor-state-rs]],
[[appimage-build-no-strip-relr-dyn-fix]], [[mcp-e2e-findings-reply-wedge-phantom-busy]].

View File

@ -0,0 +1,47 @@
---
name: checkpoint-b7-blocked-codex-session-limit
description: memory note checkpoint-b7-blocked-codex-session-limit
metadata:
type: project
---
---
name: checkpoint-b7-blocked-codex-session-limit
description: RÉSOLU 2026-07-02 — B7 (câblage runtime tâches de fond) + F1-F4 livrés, QA vert, commités ; rebuild AppImage lancé. Historique du blocage Codex conservé.
metadata:
type: reference
---
# Checkpoint — B7 + F1-F4 LIVRÉS (2026-07-02) — anciennement bloqué Codex
## RÉSOLUTION (à jour)
Codex est revenu, le cycle complet a tourné via le canal inter-agent (fiable cette fois) :
- **B7** câblé par DevBackend dans `crates/app-tauri/src/state.rs` (store per-root routé par
project_id façon AppLiveStateProvider, `AppReconcileBackgroundTasks` reconcile boot,
`AppWakeSessionProvider`, boucle ready→MediatedInbox→AgentWakeService via
`tauri::async_runtime::spawn`, `.with_background_tasks(store,clock)` au builder
OrchestratorService ~l.1858) + `commands.rs` (`reconcile_background_tasks` dans open_project).
- **F1-F4** front par DevFrontend (workStateNormalization, ProjectWorkStatePanel, LayoutGrid
badges Queued/Task done, ProjectsView toast, useProjectWorkState abonné aux events
backgroundTaskChanged/agentInboxChanged/agentWakeChanged).
- **QA vert** : app-tauri 52/52 (dont test reconcile boot dédié + 4 tests MCP périmés alignés
au nouveau protocole : idea_reply retiré→JSON-RPC -32601, idea_ask_agent exposé=13 tools,
rendez-vous capture Final inline), domain 224+12, application 5, infrastructure 30+,
frontend 449. Zéro régression (les 4 tests MCP échouaient déjà à l'identique sur base @54c8ecf).
- **Commité** (pas de push/merge sans validation user) : `e05edc6` backend (B7 + tests),
`5d88c95` frontend. `.ideai/memory` + `.ideai/proposals` volontairement non commités.
- **Rebuild AppImage** lancé : `NO_STRIP=true` (cf. [[appimage-build-no-strip-relr-dyn-fix]]).
## Réserves connues (assumées V1, = lot B8 déféré)
- Pas de `BackgroundTaskRunner` concret dans infrastructure → le sink de complétion ne s'abonne
pas à un runner réel ; complétion d'une commande `run_in_background` post-tour PAS testable
e2e (le rendez-vous inter-agent B6/HeadlessRendezvous, lui, EST vivant — c'était la douleur
principale). Boutons UI cancel/retry désactivés (pas de commande Tauri) — tooltip explicite.
- Prochain lot naturel B8 : runner de commandes couplé PTY (crée BackgroundTask{Command} au
spawn, pousse exit/stdout dans le sink) → active le flux run_in_background complet + cancel/retry.
## Historique du blocage (résolu)
Codex était en limite de session ; les 4 workers (Architect/DevBackend/DevFrontend/QA, profil
`664cc20c-…-dce4c09c3da4`) tous murés en même temps ; Main+Git sur `…dce3c09c0de4` libres.
Décision user = attendre le reset (pas d'entorse Main/Git ne code). Reset survenu → cycle repris.
Lien : [[b7-wiring-anchor-state-rs]], [[background-tasks-first-class-design]],
[[appimage-build-no-strip-relr-dyn-fix]], [[session-limit-handling-design]].

View File

@ -0,0 +1,24 @@
---
name: checkpoint-b8-command-runner-loop-closed
description: memory note checkpoint-b8-command-runner-loop-closed
metadata:
type: project
---
# B8 — Runner de commandes + boucle sink fermée (DevBackend, 2026-07-03)
Base `feature/background-tasks-first-class`. Suite de [[b8-command-runner-pty-framing]]. **Build workspace vert** ; sink tests (6) + tail units (5) + reconcile B7 verts. **Non committé** (Git tranche). QA à suivre.
## Livré (sous-tâches 1-5)
- **Domaine** : nouveau port `BackgroundCommandArchive { spec_for(TaskId)->Option<SpawnSpec> }` (ports.rs, exporté lib). Ajouté car le retry a besoin de la commande d'origine, qui n'est PAS un champ persisté du `BackgroundTask`. **À faire bénir par Architect** (ajout hors port figé).
- **Infra** : `background_task.rs` → module dir (`sink.rs` déplacé verbatim, `tail.rs`, `runner.rs`).
- `tail.rs` : `BoundedTail` ring UTF-8-safe + `bounded_tail()` + `tail_cap_bytes()` (env `IDEA_BG_TAIL_BYTES`, défaut 8 KiB, clamp [1 KiB, domaine 16 KiB]).
- `runner.rs` : `CommandBackgroundRunner` compose `Arc<dyn PtyPort>` + `Arc<dyn Clock>`. `spawn` → pty.spawn(24x80) + worker détaché ; détection de fin = EOF du stream single-consumer ; tail lu du **scrollback** (pas du stream, robuste à la course subscribe) ; exit code via `kill()` (wait après EOF naturel) ; `cancel` = flag+Notify (la branche cancel gagne la course EOF) ; deadline→Expired via select. Implémente `BackgroundTaskRunner` + `BackgroundCommandArchive`. Émet 1 complétion/tâche, n'écrit ni store ni inbox.
- **Application** : module `background` : `SpawnBackgroundCommand` (alloue TaskId, create Queued→save Running, runner.spawn ; si spawn KO → task Failed best-effort), `CancelBackgroundTask`, `RetryBackgroundTask` (nouveau TaskId, deadline **droppée** pour éviter l'expiry instantané ; réutilise `create_and_run`).
- **Composition root** (state.rs ~1545) : runner + `BackgroundCompletionSink::new(store, ready_tx.clone())` + `sink.start_from_runner(runner)` DANS `tauri::async_runtime::spawn` (Handle::current requis) ; JoinHandle du drain gardé vivant via `.await`. 4 champs AppState + use cases câblés. **Boucle B2 fermée** (avant : sink/runner uniquement en tests).
- **app-tauri** : `BackgroundTaskDto` camelCase (aplati result→exitCode/summary/tails), `parse_task_id`, commandes `spawn_background_command` (AJOUTÉE hors liste framing pour rendre la boucle atteignable/testable), `cancel_background_task`, `retry_background_task`, `list_background_tasks` — enregistrées dans invoke_handler.
## Écarts au cadrage (à arbitrer)
1. **Tee PTY live (sous-tâche 6) NON câblé** : le `Broadcast` de `PortablePtyAdapter` est **single-consumer** (chaque `subscribe_output` supersede le précédent). Un tee UI re-souscrirait et **casserait la détection EOF du runner**. Un vrai tee live exige un broadcast multi-consommateur OU un port `wait/try_wait` sur PtyPort → **décision Architect** (PtyPort figé). Documenté dans `runner.rs`.
2. **stderr_tail toujours None** : le PTY fusionne stdout/stderr → sortie fusionnée dans `stdout_tail`.
3. Port `BackgroundCommandArchive` ajouté (voir ci-dessus).
4. `list_background_tasks` sans `agentId` ne renvoie que les complétions non livrées du projet (le store ne sait pas énumérer les tâches ouvertes par projet).

View File

@ -0,0 +1,54 @@
---
name: checkpoint-issue-ticket-backend-v1-done
description: memory note checkpoint-issue-ticket-backend-v1-done
metadata:
type: project
---
---
name: checkpoint-issue-ticket-backend-v1-done
description: Checkpoint 2026-07-02 — backend V1 du système de tickets (T1-T5) terminé et vérifié vert sur feature/issue-ticket-system ; frontend F1-F5/F7 + QA formelle en attente du reset Codex.
metadata:
type: reference
---
# Checkpoint — système de tickets, backend V1 (T1-T5) fait & vérifié (2026-07-02)
Cadrage/design = [[issue-ticket-system-design]]. Branche `feature/issue-ticket-system` (base develop a9653bc).
## FAIT & vérifié (build + tests verts, harvesté sur disque)
- **T1** domaine `Issue` : `crates/domain/src/issue.rs` (+ tests `domain/tests/issue.rs`),
câblé dans `domain/src/{events.rs,ids.rs,lib.rs,ports.rs}`.
- **T2** store FS Markdown + allocator : `crates/infrastructure/src/issues.rs`
(`FsIssueStore`, `FsIssueNumberAllocator`, index.json, check version), tests `issue_store.rs`.
- **T3** use cases : `crates/application/src/issues/` (Create/Read/List/Update/UpdateCarnet/
Link/Unlink/AssignAgent, optimistic concurrency), tests `application/tests/issue_usecases.rs`.
- **T4** surface MCP : 10 outils `idea_ticket_{create,read,list,update,update_status,
update_priority,read_carnet,update_carnet,link,unlink}` enregistrés dans
`infrastructure/src/orchestrator/mcp/{mod,server,tools}.rs`.
- **T5** Tauri : `crates/app-tauri/src/tickets.rs` (commands + assign) + câblage composition-root
`crates/app-tauri/src/state.rs` (~l.824 : FsIssueStore/FsIssueNumberAllocator/use cases/
ticket_tool_provider). `cargo build` workspace OK ; app-tauri 47/51 tests verts.
## Dette pré-existante à NE PAS confondre avec régression
4 tests app-tauri rouges (`mcp_e2e_loopback_tests::{orphan_reply_is_typed_error,
ask_then_reply_round_trips_inline (+_codex)}`, `mcp_serve_peer_tests::handshake_then_tools_list`)
+ `infrastructure::orchestrator_watcher::ask_request_surfaces_reply_alongside_detail` :
tests de l'ANCIEN protocole ask/reply, échouent DÉJÀ sur `develop` vierge (prouvé par stash).
Le fix existe sur `feature/background-tasks-first-class` (alignement QA). Résorbés quand
background-tasks merge dans develop, ou à cherry-pick. NE PAS les compter contre les tickets.
## RESTE (bloqué reset Codex — les workers Architect/DevBackend/DevFrontend/QA partagent le
profil Codex ; Main+Git libres)
- **Frontend V1** : F1 gateway `IssueGateway` · F2 liste filtrable · F3 détail/édition
(conflits expectedVersion) · F4 éditeur Carnet Markdown · F5 liens & assignations · F7 `#42`
cliquable + pré-remplissage délégation. (F6 attachments = fast-follow hors V1.)
- **QA formelle** backend+frontend (create→lire via MCP, #N séquentiel, carnet édité par agent,
liens, assignation agent connu, optimistic concurrency conflit).
- Puis Git commit final + rebuild AppImage (NO_STRIP=true, cf. [[appimage-build-no-strip-relr-dyn-fix]]).
## Note process
Rendez-vous inter-agent timeout sur tâches LOURDES (binaire courant pré-B7) : l'agent produit
quand même le code, harvesté sur disque par Main (build+tests réels) puis commité via Git libre.
Confirme l'intérêt de B7/B8 (le rebuild B7 de 21:35 n'est pas encore le binaire lancé).
Lien : [[issue-ticket-system-design]], [[checkpoint-b7-blocked-codex-session-limit]],
[[background-tasks-first-class-design]].

View File

@ -0,0 +1,34 @@
---
name: checkpoint-t1-wake-delivery-bug-fixed-rebuild-pending
description: memory note checkpoint-t1-wake-delivery-bug-fixed-rebuild-pending
metadata:
type: project
---
---
name: checkpoint-t1-wake-delivery-bug-fixed-rebuild-pending
description: Ticket #1 — T1 VERT live. T3 root cause = tâches de fond jamais projetées dans work-state ; fix BE+FE livré unit-green ; rebuild AppImage en cours ; reste re-QA live T3 + T2 reboot, puis merge.
metadata:
type: project
---
# Checkpoint ticket #1 — état au 2026-07-03
## T1 — VERT (validé live QA)
Wake auto du propriétaire à la complétion + `completionDelivered: true` prouvés dans le store (`.ideai/background-tasks/<owner>.json`, task `02aa2493`). Fixes portant T1 (unit-green, NON commités) : wake.rs:119 (mark_completion_delivered dès session.send accepté), structured.rs:121 (drain_reply_stream_with_readiness), agent_wake.rs:504 (régression), + itér.1 MediatedInbox enqueue FIFO sans busy.
Note : `idea_workstate_read` n'expose PAS les BackgroundTasks — lire le store JSON.
## T3 — root cause trouvée + fix livré (unit-green, NON commité)
Défaut live : aucun bouton Cancel/Retry dans le panneau Work. Cause = le DTO `AgentWorkState` ne portait pas les tâches de fond et `GetProjectWorkState` n'avait pas de dépendance BackgroundTaskStore → `agent.backgroundTasks` toujours vide en live → section masquée (ProjectWorkStatePanel.tsx:638). Front déjà câblé, seule la projection backend manquait. Vitest verts en trompe-l'œil (payload mocké).
Cadrage Architect : mémoire `workstate-background-tasks-projection-fix` (Option A — étendre le read-model unique, best-effort, aucun nouveau port).
Livré :
- BE (DevBackend) : VO `AgentBackgroundTaskState` + champ `background_tasks` sur AgentWorkState + builder `with_background_tasks` + projection union open/undelivered best-effort (workstate/mod.rs) ; DTO Tauri backgroundTasks (dto.rs) ; wiring state.rs. Tests : `cargo test -p application` 24 passed (workstate.rs), `-p app-tauri` vert, build workspace OK.
- FE (DevFrontend) : mapping queued/waiting→pending explicité, test sur vrai shape backend, + correctif tri (finishedAtMs jamais émis → bascule sur updatedAtMs). vitest 460/460, build OK. Verdict : `tickets-t3-frontend-validation-verdict`.
- Dette de contrat tracée en ticket #5 (summary non affiché, owner/project inutiles). Low.
## RESTE À FAIRE (ordre)
1. Rebuild AppImage `NO_STRIP=true` — EN COURS (background bixa34tht). Voir [[appimage-build-no-strip-relr-dyn-fix]].
2. Utilisateur relance l'AppImage.
3. QA re-passe T3 LIVE : T3-a apparition running + Cancel actif ; T3-b Cancel agit → cancelled + Retry actif ; T3-c Retry → nouvelle tâche running ; T3-d borne livraison (tâche livrée disparaît = conforme V1, PAS un bug) ; T3-e isolation par agent ; T3-f non-régression live/busy/tickets. Détail dans cadrage Architect.
4. T2 — reconcile après reboot (fin AVANT wake), protocole MANUEL, coordination reboot utilisateur.
5. Si T1+T2+T3 verts → Git commit de TOUS les fix (T1 + T3 BE/FE) + merge feature/background-tasks-first-class → develop (résorbe aussi 4 tests protocole MCP périmés rouges sur develop).
Liens : [[workstate-background-tasks-projection-fix]], [[tickets-t3-frontend-validation-verdict]], [[b8-in-app-trigger-run-in-background]], [[background-tasks-first-class-design]].

View File

@ -0,0 +1,52 @@
---
name: develop-realigned-to-cli-ui-baseline-2026-07-02
description: memory note develop-realigned-to-cli-ui-baseline-2026-07-02
metadata:
type: project
---
# develop réaligné sur la ligne UI CLI (pre-chat-ui-baseline) — 2026-07-02
**Type :** décision de topologie, validée utilisateur.
## Contexte / erreur corrigée
La feature « annonces inter-agent + fix Final Codex » avait été branchée par erreur sur
`develop` @ `0072aff`, qui portait le chantier **canonical-conversation / chat** (LC1→LC5).
Or l'UI de référence de l'utilisateur est la **ligne CLI** portée par
`feature/pre-chat-ui-baseline` @ `a9653bc` (UI CLI + commit « délégation toujours headless,
jamais d'injection PTY »). Le rebuild sur develop avait régressé l'UI (plus de terminaux CLI).
De plus, `a9653bc` — prémisse runtime de l'overlay — n'était QUE sur pre-chat-ui-baseline.
## Opération effectuée (Git, réversible, locale, aucune sortante)
1. Secours du chantier chat : branche `snapshot/develop-canonical-conversation-0072aff` +
tag homonyme → `0072aff` (récupérable à 100%).
2. `git branch -f develop feature/pre-chat-ui-baseline`**develop == `a9653bc`** (arbre
exactement celui de la ligne CLI). Le chantier chat est SUPERSÉDÉ sur la mainline develop
mais non détruit.
3. Nouvelle branche de fix : **`feature/inter-agent-announcements-v2`** depuis le nouveau
develop (@ a9653bc).
Retour arrière : `git branch -f develop snapshot/develop-canonical-conversation-0072aff`.
## État des refs
- `develop` = `a9653bc` (base de travail canonique désormais = ligne CLI).
- `feature/inter-agent-announcements-v2` = base de travail pour reporter les fix.
- `feature/inter-agent-announcements` = `71d307d` : ancien commit B0-B3 (basé sur l'ancien
develop chat), conservé pour cherry-pick.
- `snapshot/develop-canonical-conversation-0072aff` = chantier chat préservé.
## Conséquences pour la suite
- Reporter **B1/B2/B3** sur `feature/inter-agent-announcements-v2` (cherry-pick depuis
71d307d). **B0 devient probablement REDONDANT** : `a9653bc` (headless alongside/toujours
headless) est déjà la base — à vérifier avant de le rejouer.
- **Re-planifier F1/F2/F3** : le design frontend (preview cellule demandeur + overlay cellule
cible) avait été cadré contre le modèle de cellules canonical-conversation de develop ; sur
la base CLI (`a9653bc`), il faut re-cibler le modèle de cellules CLI/PTY réel. À faire
cadrer par Architect avant dev frontend.
- Rebuild AppImage à faire depuis cette base → restaure l'UI CLI + embarque les fix.
Liens : [[inter-agent-announcements-feature-and-codex-final-bug]],
[[inter-agent-live-context-shared-per-agent]].

View File

@ -0,0 +1,13 @@
---
name: f35-config-crud-delivered
description: F35.2 (config serveur local + sélection dans les profils OpenCode) livrée et verte sur feature/modeles-locaux ; clôt le sprint modèles locaux F34-F36.
metadata:
type: project
---
Ticket #35 F35.2 LIVRÉ (vert) sur `feature/modeles-locaux`. Débloqué par le contrat PLAT d'Architect.
**Contrat backend** : `list_model_servers` / `save_model_server({request:{config}})` / `delete_model_server({serverId})`. DTO plat camelCase `LocalModelServerConfig { id, kind:"llamaCpp", name, baseURL, port, modelPath?, servedModelName, binaryPath?, args, autoStart, stopPolicy }`. PIÈGE intégré : `save_model_server` exige un UUID valide dans `config.id` même à la création (backend ne génère PAS l'id serveur, seulement le model.id interne caché) → le front minte l'UUID client-side (`newModelServerId`). Erreur suppression référencée = `code:"model_server_in_use"`.
**Livré** : `ModelServerGateway` (port + adapter Tauri `adapters/modelServer.ts` + `MockModelServerGateway` avec `markInUse`). Feature `features/model-servers/` : `useModelServers` (CRUD, traduit in-use en message FR actionnable), `ModelServersPanel` (déclarer/éditer/supprimer), `ModelServerSelect` (dropdown binding avec « None » = serveur externe + préserve un id orphelin « unknown/removed »). Dans `FirstRunWizard`, le champ texte `localModelServerId` (F36) est remplacé par `ModelServerSelect`.
Cohérent avec F35.1 (badge corrèle via localModelServerId). tsc 0 ; suite 608/608. Sprint modèles locaux (F34 backend, F35, F36) désormais complet côté frontend.

View File

@ -0,0 +1,16 @@
---
name: f35-launch-status-done-config-crud-blocked
description: F35.1 UI statut de lancement du serveur local livrée+verte ; F35.2 config CRUD bloquée sur un DTO figé qui ne matche pas le wire réel + delete manquant.
metadata:
type: project
---
Ticket #35 sur `feature/modeles-locaux`.
**F35.1 LIVRÉ (vert)** : événements `model_server_status_changed`/`agent_launch_failed` déjà exposés via `domain://event`. Consommés dans `useAgents``modelServerStatusByServer` (par serverId) + `launchFailureByAgent` (par agentId). Badge `ModelServerLaunchBadge` monté par ligne dans `AgentsPanel`, corrélé via `profile.opencode.localModelServerId`. Contrat réel ≠ DTO figé du ticket : `serverId` sur l'enveloppe (pas par état) ; états camelCase `notConfigured|probing|starting|ready{reused}|failed{code,message}` ; PAS de `baseURL`/`model` émis dans le statut. Je me suis aligné sur le wire réel.
**F35.2 BLOQUÉ — 3 gaps backend remontés à Main :**
1. (bloquant) `save_model_server`/`list_model_servers` transmettent le domaine `LocalModelServerConfig` en `serde(transparent)`, forme IMBRIQUÉE : `{id, kind:"llamaCpp", name, endpoint:{baseURL,port}, model:{id,label,path?,servedName}, binary?, args, autoStart, stopPolicy}`. Le DTO figé du ticket est plat et FAUX (kind "llamacpp", baseURL/port/modelPath/servedModelName/binaryPath à plat). Pire : `model` exige `id`+`label` non-vides (LocalModelRef::new) absents du DTO figé → payload figé rejeté. Besoin arbitrage Architect : (A) couche DTO plate app-tauri qui remplit model.id/label, ou (B) MAJ du DTO figé vers la forme imbriquée + sémantique de model.id/label.
2. Pas de commande `delete_model_server`.
3. (côté moi) créer `ModelServerGateway`/port/adapter une fois le contrat tranché.
Saisie simple de `localModelServerId` (F36) laissée en place, pas de régression. tsc propre ; suite 593/593.

View File

@ -0,0 +1,17 @@
---
name: f36-multi-opencode-profiles-frontend
description: Multiplicité des profils OpenCode locaux côté UI first-run wizard — livrée, verte, sans gap backend.
metadata:
type: project
---
Ticket #36 (F36.1) livré côté frontend sur `feature/modeles-locaux`.
**Constat clé** : aucun gap backend. Le backend supporte déjà N profils OpenCode (identité = ProfileId) via `clone_opencode_profile_from_seed`, `list_profiles`, `save_profile`, `delete_profile`, `configure_profiles`. `OpenCodeConfig` DTO expose `localModelServerId` (camelCase).
**Ce qui manquait, purement front** : la commande clone n'était câblée dans aucun gateway, et le miroir TS `OpenCodeConfig` n'avait pas `localModelServerId`.
**Livré** : `ProfileGateway.cloneOpenCodeProfileFromSeed` (port + adapter Tauri + mock) ; le first-run wizard (`FirstRunWizard.tsx` + `useFirstRun.ts`) gère désormais une LISTE de profils OpenCode — bouton "Add OpenCode profile" + "Duplicate" par ligne, name éditable, cases reasoning/attachment, champ localModelServerId (saisie simple).
**Reste** : lot F35 = UI riche de config serveur local (remplacera la saisie simple de `localModelServerId`). Le wizard reste la surface de gestion de profils (`Settings ▸ Configure profiles` = `forceOpen`) ; il lit `firstRunState().referenceProfiles`, pas `list_profiles` — à vérifier au lot F35 pour l'édition de profils OpenCode déjà persistés.
tsc propre ; 62 tests first-run/mock verts ; suite complète 581/581.

View File

@ -0,0 +1,11 @@
---
name: floatingwindow-focus-trap-mount-only
description: Le focus-trap de FloatingWindow ne doit dépendre que du mount ; sinon un onClose instable vole le focus à chaque frappe.
metadata:
type: reference
---
Le primitif `frontend/src/shared/ui/FloatingWindow.tsx` a un `useEffect` focus-trap qui appelle `(first ?? node)?.focus()`. Sa dépendance doit rester `[]` (mount-only), et `onClose` doit être lu via un `onCloseRef` maintenu à jour.
**Pourquoi** : les appelants (ex. `TicketDetail.requestClose`) passent souvent un `onClose` recréé à chaque render. Si l'effet dépend de `[onClose]`, chaque frappe dans un input contrôlé de la fenêtre → re-render → nouvelle identité `onClose` → l'effet se ré-arme → focus renvoyé au premier focusable → l'utilisateur ne peut taper qu'UNE lettre à la fois (bug #17, corrigé 2026-07-07).
**Comment l'appliquer** : ne jamais remettre `onClose` (ni une autre prop changeante) dans les deps de cet effet ; router les callbacks via une ref. Régression couverte par le test « #17 focus-loss bug » dans FloatingWindow.test.tsx. Voir [[ticket16-menus-floating-delivered-devfrontend]].

View File

@ -0,0 +1,67 @@
---
name: inter-agent-announcements-feature-and-codex-final-bug
description: memory note inter-agent-announcements-feature-and-codex-final-bug
metadata:
type: project
---
# Annonces inter-agent (UI live) + fix du Final Codex — DESIGN FIGÉ
**Type :** design de feature + bug racine. Validé utilisateur le 2026-07-02, cadré par Architect.
## Bug racine (Codex)
`crates/infrastructure/src/session/codex.rs` : `parse_event` (~l.86) mappe CHAQUE
`item.completed{item.type=="agent_message"}` vers `ReplyEvent::Final`, et `send` (~l.234)
casse au PREMIER Final (`break 'lines`). Or `codex exec --json` émet plusieurs
`agent_message` par tour (préambule → outils → conclusion → `turn.completed`). IdeA
renvoyait donc le **préambule** au demandeur au lieu de la **conclusion**. Preuve live :
Architect répondait « je vais interroger QA… » au lieu du résultat. **Défaut d'implémentation,
pas du headless Codex** (un `codex exec` brut déroule tout le tour).
## Feature validée (le bug devient fonctionnalité)
- **Final** = DERNIER `agent_message` avant `turn.completed` → seule chose renvoyée au
demandeur (résout le ticket). Bords : 1 seul message (préambule=conclusion) ; 0 message
`TargetReturnedNoReply` (inchangé).
- **Annonces** = `agent_message` intermédiaires → nouvel événement NON terminal
`ReplyEvent::Announcement{text}`, poussé sur le bus vers l'UI, JAMAIS renvoyé au
demandeur, JAMAIS persisté au transcript (éphémère).
- **UI** : (a) cellule DEMANDEUR = petit cadre preview près de la drop-list d'agents,
filtré par `ticket_id` ; (b) cellule CIBLE si visible = overlay d'indisponibilité (texte
haut « un agent est en train de lui parler » + annonces défilantes au centre), monté sur
live-state Working, retiré au Final/idle (reste tant qu'≥1 ticket actif sur la cible).
## Contrat (Architect)
- `ReplyEvent::Announcement{text}` (non terminal) vs `Final{text}` (terminal, résout).
- `drain_with_readiness` : Announcement → publie event, ne résout pas, ne passe pas idle ;
Final → résout ticket + idle + fin du drain.
- Nouveau `DomainEvent::AgentAnnouncement{project_id, requester, target, ticket_id, text,
at_ms}` (attribution obligatoire pour router vers la bonne cellule + isoler projet/onglet).
- Overlay cible = état UI piloté par live-state (pas un blocage du PTY : le contact passe par
session headless séparée, `allow_structured_alongside_pty:true`).
- Généralisation Claude : `session/claude.rs` garde son terminal explicite (`result`) comme
Final ; messages complets non terminaux → Announcement ; NE PAS promouvoir les `TextDelta`
tokenisés (bruit) ; tool events restent `ToolActivity`.
## Découpage dev (Architect)
- **B1** domaine/appli : `ReplyEvent::Announcement`, `DomainEvent::AgentAnnouncement`,
`structured.rs`, `service.rs` (`ask_structured` ne termine que sur Final).
- **B2** Codex `codex.rs` : bufferiser le dernier `agent_message`, émettre les précédents en
Announcement, le dernier en Final à `turn.completed`.
- **B3** Tauri `app-tauri/{events,dto,state}.rs` : relay + DTO camelCase.
- **F1** front store/gateway annonces (index par ticket/target, borné ~20-50).
- **F2** front preview cellule demandeur.
- **F3** front overlay cellule cible.
- **Q** QA e2e : Main→Architect→QA, préambule+outil+conclusion ; Main ne reçoit que la
conclusion ; preview demandeur + overlay cible pendant le travail ; fermeture au Final.
Critère : reproduire le bug initial et constater la correction.
## État
Design figé, cadrage Architect complet. Reste : Git branche → B1/B2/B3 + F1/F2/F3 → QA.
Non démarré côté dev au 2026-07-02.
Liens : [[inter-agent-live-context-shared-per-agent]], [[rendezvous-no-reply-backstop-design]],
[[headless-interagent-conversation-objective]], [[conversation-viewer-ls7-frontend]].

View File

@ -0,0 +1,79 @@
---
name: inter-agent-live-context-shared-per-agent
description: memory note inter-agent-live-context-shared-per-agent
metadata:
type: project
---
# Contexte live inter-agent : PAR AGENT (partagé), pas par paire — DÉCISION FIGÉE
**Type :** decision d'architecture / produit — validée utilisateur le 2026-07-02.
## Décision
Le contexte live d'un agent IdeA est porté **par l'agent cible**, partagé entre TOUS les
demandeurs. Pour un `AgentId` donné, IdeA maintient **au plus une** session
headless/structurée vivante ; tous les `idea_ask_agent(requester, target)` vers le même
`target` réutilisent cette session, quel que soit le demandeur.
Ce partage est **intentionnel et souhaité**, pas un bug ni une fuite : tous les agents du
projet sont dans le même domaine de confiance (même utilisateur, même projet). Le contexte
live d'un agent = mémoire de travail d'équipe / tableau blanc partagé. On NE veut PAS
d'isolation par paire.
La paire `(requester, target)` n'est **pas** une frontière d'isolation du contexte moteur :
elle sert uniquement à l'attribution, la corrélation requête/réponse, les vues UI et les
filtres de transcript.
## Preuve empirique (2026-07-02)
Main a confié à QA le token `IDEA-TOKEN-7F3A-BLEEDCHECK` ; Architect a ensuite pu le
récupérer auprès de QA **sans que Main le lui transmette**. Cause : `ensure_structured_session`
(`crates/application/src/orchestrator/service.rs:1948`) fait un early-return sur
`session_for_agent(&agent_id)` → une seule session vivante par agent, réutilisée.
## Modèle de transcript cible (cadré par Architect)
**Log canonique PAR AGENT + vues par paire dérivées.** Abandon du transcript canonique
par paire comme source de vérité (il promet une isolation qui n'existe pas en live).
- Arbo cible : `.ideai/conversations/agents/<targetAgentId>/{log.jsonl, handoff.md, providers.json, log.N.jsonl}`
- Chaque tour porte l'attribution : `thread_id=Agent(target)`, `requester`, `target`,
`correlation_id`, `role`, `source`.
- Vues = projections filtrées (`ForAgent`, `ForPair`, `ForCorrelation`), pas des logs séparés.
Impacts à traiter :
- `resolve_conversation``resolve_agent_thread(target)` + `resolve_conversation_view(requester,target)`.
- `bind_conversation_session` → bind sur `targetAgentId` ; réutiliser la session existante.
- `ConversationRegistry` → index primaire `targetAgentId`, secondaires `requester`/`correlation_id` ;
interdire 2 fils live pour le même target.
- `ProviderSessionStore` clé `(agent_thread_id, provider_id)` au lieu de `(pair_id, provider_id)`.
- `LeafCell.conversation_id` = désormais **id de fil agent**, plus « id de paire ».
- Viewer LS7 : défaut = fil agent complet ; vue par paire = vue filtrée/partielle libellée comme telle.
## Garde-fou efficience (rotation multi-demandeurs)
Le design LS5/LS6 tient, mais un fil agent partagé **croît plus vite** (agrège les demandes
de plusieurs requesters). Ajustements : la rotation s'applique au fil agent canonique (pas
aux vues) ; le résumé/handoff doit conserver l'attribution minimale (`Objectif courant`,
`Demandes actives par requester`, `Décisions récentes`, `Blocages`, `Dernière réponse
corrélée`) ; transcript brut jamais réinjecté (humain-only) ; handoff borné à
`HANDOFF_SUMMARY_MAX_CHARS=4096`. Risque principal : dilution si demandeurs poussent des
objectifs contradictoires → mitigé par les rubriques stables du handoff.
## Découpage dev recommandé (Architect)
1. `ARCHITECTURE.md` §19.7 : remplacer « id de paire » par « fil agent partagé » (+ §16/§17/§21).
2. Introduire `ConversationThreadId` / `ConversationViewId`.
3. Adapter `resolve_conversation`, `bind_conversation_session`, `ConversationRegistry`.
4. Compatibiliser les anciens chemins `for_pair(requester,target)` comme vues historiques.
5. LS7 viewer : fil agent complet + filtres.
6. Test QA : un fait confié à QA par Main est visible quand Architect interroge QA.
## État
Décision figée. Cadrage architectural livré par Architect. Reste à : intégrer la proposition
dans `ARCHITECTURE.md`, puis planifier le dev (cycle normal Architect→Git→Dev→QA).
Liens : [[conversation-rotation-safety-design]], [[handoff-ls5-summary-bound-and-llm-seam]],
[[conversation-log-ls6-rotation-and-paginated-read]], [[conversation-viewer-ls7-frontend]],
[[headless-interagent-conversation-objective]].

View File

@ -0,0 +1,78 @@
---
name: issue-ticket-system-design
description: memory note issue-ticket-system-design
metadata:
type: project
---
---
name: issue-ticket-system-design
description: Cadrage figé (Architect+Main, 2026-07-02) du système de tickets IdeA façon Jira — domaine `Issue` par projet, exposé « ticket » côté UI/MCP, Carnet éditable, #N séquentiel. Fait autorité pour les lots T1-T6/F1-F7.
metadata:
type: reference
---
# Système de tickets IdeA (domaine `Issue`) — CADRAGE FIGÉ 2026-07-02
## Décisions produit verrouillées (utilisateur)
- **Portée PAR PROJET** : stockage dans `.ideai/tickets/` du projet (pas de backlog global).
- **Identifiant court `#42`** : compteur séquentiel par projet, parlable à l'oral (poignée
partagée humain↔agent, y compris dans une délégation `idea_ask_agent`).
- **Champ de connaissances éditable = « Carnet »** (PAS « mémoire » — évite collision avec la
mémoire projet). Markdown éditable/réorganisable, scoped ticket, pas append-only.
- **Stockage Markdown + frontmatter**, un DOSSIER par ticket.
- **V1 SANS attachments** : livrer T1-T5 + F1-F5 + F7 ; attachments (T6/F6) en fast-follow.
## Nommage (collision évitée avec le TicketId de délégation)
Domaine/code = **`Issue`** (`IssueId`, `IssueNumber`, `IssueRef("#42")`, `IssueStore`,
modules `domain::issue`/`application::issues`/`infrastructure::issues`). UI + MCP public =
« ticket » (`idea_ticket_*`, DTO camelCase). Ne JAMAIS nommer `Ticket` en code (réservé à la
délégation inter-agent).
## Frontière Carnet vs mémoire projet
Carnet = savoir DU ticket (décisions locales, hypothèses, investigation, contraintes). Mémoire
projet = savoir transverse durable. Règle : si l'info doit survivre à la fermeture du ticket et
guider d'autres travaux → mémoire projet ; sinon → carnet.
## Modèle domaine
`Issue { id(uuid), number(u64), title, description(md), status, priority, carnet, links,
agent_refs, attachments, created_by/updated_by(IssueActor: User|Agent|System),
created_at/updated_at, version(optimistic) }`.
- `IssueStatus`: Open | InProgress | Qa | Closed (DTO: open|inProgress|QA|closed).
- `IssuePriority`: Low | Medium | High | Critical.
- `IssueLinkKind`: RelatesTo | Blocks | BlockedBy | Duplicates | DependsOn.
- `AgentIssueRef { agent_id, role: Assigned|Mentioned|Reviewer|Owner }`.
Invariants : number>0 unique/projet ; IssueRef = `#<u64>` ; titre non vide ; pas de self-link ;
assignation → AgentId connu du manifeste ; toute écriture incrémente version ; attachments sous
le dossier du ticket, pas de `..`/symlink/chemin absolu.
## Ports
`IssueStore` (create/get_by_ref/list(filter)/update(expected_version)/read_carnet/
write_carnet(expected_version)) ; `IssueNumberAllocator` (allocate_next, atomique via
counter.json + counter.lock + rename, jamais de réutilisation de numéro) ; `IssueAttachmentStore`
(add/remove/list — lot attachments). Adapters : `FsIssueStore`, `FsIssueNumberAllocator`,
`FsIssueAttachmentStore`, `McpIssueTools`, `TauriIssueCommands`, `TauriIssueEventRelay`.
## Surface MCP (agents)
`idea_ticket_create/read/list/update/update_status/update_priority/read_carnet/update_carnet/
link/unlink` (+ `idea_ticket_attachment_*` au lot attachments). DTO camelCase ; `TicketRef` =
`#${number}` ; concurrence via `expectedVersion` sur chaque écriture.
## Events domaine (→ projection UI, même modèle events→front B-series)
IssueCreated/Updated/StatusChanged/PriorityChanged/CarnetUpdated/Linked/Unlinked/
AgentAssigned/AgentUnassigned/AttachmentAdded/Removed/StorageConflictDetected.
## Layout stockage
`.ideai/tickets/{counter.json, index.json(projection reconstructible), <N>/{issue.md(frontmatter
+description), carnet.md, attachments/}}`. Attachments versionnés git par défaut (warning >5MiB,
refus configurable >25MiB, pointeur URI pour les gros).
## Lots
Backend : T1 domaine Issue · T2 store FS Markdown (+allocator) · T3 use cases (Create/Read/List/
Update/UpdateCarnet/Link/AssignAgent, optimistic concurrency) · T4 surface MCP `idea_ticket_*` ·
T5 commands Tauri + relay events · T6 attachments (fast-follow).
Frontend : F1 gateway UI (`IssueGateway`, pas d'invoke direct) · F2 liste filtrable · F3 détail/
édition (gestion conflits expectedVersion) · F4 éditeur Carnet Markdown · F5 liens & assignations ·
F6 attachments (fast-follow) · F7 `#42` cliquable + pré-remplissage délégation.
Réutilise : store FS par projet façon `FsBackgroundTaskStore`/`AppLiveStateProvider`, EventBus +
TauriEventRelay, pattern gateway UI.
Lien : [[background-tasks-first-class-design]], [[agent-context-memory-and-profile-handoff]].

View File

@ -0,0 +1,35 @@
---
name: opencode-llamacpp-replaces-ollama
description: memory note opencode-llamacpp-replaces-ollama
metadata:
type: project
---
---
title: OpenCode local provider — llama.cpp remplace Ollama (chantier livré)
type: decision
description: Le provider local d'OpenCode passe d'Ollama à llama.cpp (llama-server OpenAI-compatible). Profil éditable baseURL/apiKey/model. Livré sur develop (merge 2e98f1f). Clôt le diagnostic tool-calling Ollama.
---
# OpenCode local : llama.cpp remplace Ollama (2026-07-11)
## Pourquoi
Le tool-calling des modèles locaux via **Ollama** ne s'est jamais déclenché nativement en live : OpenCode fuyait les appels d'outils en texte `<function=...>` (émulation par prompt), jamais parsés. Diagnostic utilisateur : la faute est dans la relation **OpenCode↔Ollama**. Décision : abandonner Ollama comme provider OpenCode et passer à **llama.cpp** (`llama-server`, endpoint OpenAI-compatible). Voir historique : [[opencode-config-model-agnostic-tool-diagnostic]], [[opencode-ollama-native-tool-call-flag]], [[opencode-ollama-tool-calling-surface-overload]] (contexte Ollama, désormais superseded).
## Contrat livré
`OpenCodeConfig` (domaine + DTO app-tauri + type frontend) :
`{ baseURL: String (requis, http/https), apiKey: Option<String>, model: String (requis), reasoning: Option<bool>=true, attachment: Option<bool>=false }`. Sérialisation wire camelCase (`baseURL` via serde rename), `apiKey`/`reasoning`/`attachment` `skip_serializing_if=None`.
Seed profil canonique : `opencode-ollama`**`opencode-llamacpp`** ("OpenCode + llama.cpp"), command `opencode`, adapter `OpenCode`, defaults `baseURL=http://localhost:8080/v1`, `apiKey=sk-no-key`, `model=qwen3-coder-30b`.
`opencode_config_json` (crates/application/src/agent/lifecycle.rs) régénère un opencode.json MINIMAL : provider `llamacpp` (`npm=@ai-sdk/openai-compatible`, `options.baseURL/apiKey` du profil, def modèle `llamacpp/<model>` avec `tool_call:true,reasoning,attachment`), MCP `idea` inchangé, `permission bash/edit=ask`, `disabled_providers=["anthropic","openai","gemini","ollama"]`. `apiKey` omis (pas `null`) si absent. Plus aucun résidu `ollama`/préfixe `ollama/`/allow-list diagnostic.
Frontend : wizard profil OpenCode = 3 champs éditables **Base URL / Model / API key(optionnel)** (`FirstRunWizard.tsx`, `profile.ts`, mocks). L'embedder Ollama (`ollamaDetected`, mémoire vectorielle) est un AUTRE usage, NON touché.
## État
- Fichiers : domain/profile.rs, application/{catalogue.rs,lifecycle.rs,tests/agent_lifecycle.rs}, infrastructure/session/opencode.rs ; frontend domain/index.ts, adapters/mock/*, features/first-run/*.
- QA VERT : domain 244, application 81+64, infra 263/10 (les 10 = bind-port sandbox, identiques sur develop = non-régression), frontend 574, tsc propre. Claude/Codex non régressés.
- Git : commit `4e70631`, merge `--no-ff` `2e98f1f` sur `develop`. Base branche = `eaba05d` (profil OpenCode process-backed). WIP diagnostic Ollama capsulé dans `3cdedf2` sur `feature/opencode-glm47-flash-tool-diagnostic`. PAS de push distant.
- Tickets : #32 fermé ; #42/#43 (Ollama) retirés du store.
## Reste ouvert
E2E live llama.cpp jamais exécuté (aucun `llama-server` joignable sur :8080 au moment QA ; `llama-server` 9859 + `opencode` 1.17.18 installés). À valider côté utilisateur : lancer llama-server, relancer l'Appointe rebuild, créer un agent profil llama.cpp, vérifier un vrai `tool_use` MCP (pas de `<function=` texte). Rebuild AppImage requis pour le live (binaire qui tourne = AppImage). Build via `NO_STRIP=true` cf [[appimage-build-no-strip-relr-dyn-fix]].

View File

@ -0,0 +1,64 @@
---
name: ticket14-local-lan-openai-adapter-scoping
description: memory note ticket14-local-lan-openai-adapter-scoping
metadata:
type: project
---
# Ticket #14 — cadrage figé : adapter IA local/LAN OpenAI-compatible canonique
Périmètre figé (review agent-user faite) : canal = **adapter HTTP natif** parlant une API compatible OpenAI/Ollama ; usage **transparent** vs Claude/Codex (mêmes cellules, conversation headless canonique, historique, reprise, délégation, vivacité) ; **PUREMENT ADDITIF** (zéro régression Claude Code / Codex CLI) ; **parité tool-calling/MCP** dès ce ticket avec **dégradation propre** si le modèle n'expose pas les outils.
## 0. Asymétrie structurelle (commande TOUT le design)
| | Claude/Codex (existant) | Modèle local HTTP (#14) |
|---|---|---|
| Nature | binaire CLI spawné | serveur HTTP appelé |
| Boucle agentique | conduite par la CLI | conduite par **IdeA** |
| Rôle MCP d'IdeA | serveur ; CLI = client | pas de client ⇒ IdeA = orchestrateur d'outils |
| Contexte .md | la CLI lit son convention file | injecté en **system message** par l'adapter |
| Conversation | id moteur (session_id/thread_id) | API **stateless** ⇒ transcript possédé par IdeA |
Conséquence : le nouvel adapter **n'utilise PAS** `session/process.rs` (run_turn/spawn/drain). Il ne partage avec claude.rs/codex.rs que les **types de port** (`AgentSession`, `ReplyEvent`). Cette absence de code partagé mutable EST la garantie d'isolation.
## 1. Cartographie hexagonale
**Domaine (crates/domain)**
- Variante `StructuredAdapter::OpenAiCompatible` (profile.rs:244). `provider_key()``"openai-compatible"` (contrat persistance providers.json). Sérialise camelCase `"openAiCompatible"`.
- Nouveau VO validé (parse-don't-validate) `HttpChatConfig { endpoint:String (http/https non vide), model:String (non vide), api_key_env:Option<String> (nom de var env valide, JAMAIS la clé), request_timeout_ms:Option<u32>, connect_timeout_ms:Option<u32>, max_tool_iterations:Option<u16> (garde-fou boucle, défaut ~16) }`. Le domaine ne résout jamais la clé (pas d'I/O env), il porte le **nom** de variable.
- Rattachement `AgentProfile.chat_http: Option<HttpChatConfig>` avec `#[serde(default, skip_serializing_if="Option::is_none")]` (miroir exact de `mcp`/`liveness`/`rate_limit_pattern`, profile.rs:570-604) ⇒ **zéro régression de sérialisation** (profils Claude/Codex bit-identiques). Test round-trip obligatoire (miroir profile_without_mcp_round_trips_identically).
**Nouveau port tool-calling (le seam clé — le pont MCP .mcp.json/config.toml + bridge `idea mcp-server` NE s'applique pas, pas de client CLI)**
```rust
pub struct ToolSpec { name:String, description:String, input_schema:serde_json::Value }
#[async_trait] pub trait ToolInvoker: Send+Sync {
fn tools(&self) -> Vec<ToolSpec>;
async fn call(&self, name:&str, args_json:&str) -> Result<String, ToolInvocationError>;
}
```
Implémenté en **app-tauri** en déléguant à la **même** `OrchestratorService::dispatch` (orchestrator/service.rs:1082) que le endpoint MCP ⇒ parité délégation/rendez-vous (idea_ask_agent/idea_reply), gating permissions/sandbox conservé. Injecté dans la factory via `with_tool_invoker(Arc<dyn ToolInvoker>)` (jumeau de `with_sandbox_enforcer`). `None` ⇒ tool-calling désactivé proprement (chat nu).
**Infra** : nouveau fichier `crates/infrastructure/src/session/openai_compat.rs` (jumeau structurel de codex.rs SANS process). Client HTTP **reqwest + rustls-tls** (jamais native-tls — spike AppImage §13-2, confiné au crate infra). Parsing isolé pur `parse_chat_delta`/`parse_completion` (miroir de parse_event) — seul endroit qui connaît le schéma OpenAI (choices[].message, tool_calls[], SSE data:).
**Routing — seul contact avec l'existant** : `StructuredSessionFactory::start` (factory.rs:111) = **UN bras de match ajouté** ; bras Claude/Codex INTOUCHÉS ; `supports()` reste `profile.is_selectable()` (=structured_adapter.is_some(), profile.rs:857) ⇒ sélectionnable sans changer le prédicat.
## 2. Contrat de session headless
`send(prompt)` : 1) transcript.push(user) ; 2) émettre Heartbeat ; 3) POST {endpoint}/chat/completions {model, messages:transcript, tools?:invoker.tools(), stream:true} ; 4) si tool_calls ⇒ pour chaque: ToolActivity{label=name} + result=invoker.call(name,args) + push(assistant tool_call)+push(tool result), reboucler (borné max_tool_iterations) ; 5) sinon streamer chunks ⇒ TextDelta (+tap→Announcement, cf claude.rs:337) ; 6) réponse complète ⇒ push(assistant) + **Final{content}** ; 7) persister transcript run dir. Contrat de flux universel respecté (un seul Final terminal) ⇒ send_blocking/drain_with_readiness marchent tels quels.
**Erreurs (jamais de corps HTTP brut propagé)** : endpoint injoignable 1er contact/probe ⇒ `AgentSessionError::Start` (UI erreur, IdeA non bloqué) ; réseau/coupure en tour ⇒ `Io` ; JSON illisible/schéma ⇒ `Decode` (diagnostic court) ; timeout ⇒ `Timeout` via send_blocking, session non tuée ; modèle absent (404/400) ⇒ `Start` dédié.
**Reprise sans mélange d'ids (invariant du ticket)** : /chat/completions **stateless** ⇒ AUCUN id provider ⇒ `conversation_id()` retourne **None** (rien à mélanger). État = transcript provider-shaped (roles+tool_calls) **possédé par IdeA**, persisté dans le **run dir stable** `.ideai/run/<agent-id>/chat-transcript.json` (clé = agent, jamais id provider). Reprise = ré-instanciation factory ⇒ rechargement transcript ; `seed_conversation_id` (factory.rs:63) **ignoré** par cet adapter (documenter). DISTINCT du conversation-log LS6 (log.jsonl, dérivé ReplyEvent, surface humaine, inchangé) — deux artefacts, aucun mélange.
**Contexte** : `start` reçoit `ctx:&PreparedContext` ; `ctx.content` (ports.rs:120) = Markdown rendu ⇒ injecté comme premier message **system** (le serveur HTTP ne lit pas de convention file).
## 3. Seam tool-calling / dégradation
Parité : ToolInvoker câblé ⇒ modèle local voit idea_* via la même OrchestratorService (délégation/rendez-vous/contexte/mémoire/tickets identiques, gating conservé). Dégradation 3 niveaux : (1) ToolInvoker None ⇒ chat nu ; (2) profil opte sans outils ⇒ idem ; (3) endpoint rejette param `tools` ⇒ détecter, retirer tools, rejouer chat nu, 1 diagnostic — l'agent converse toujours, sans déléguer. Garde-fou max_tool_iterations (modèles locaux moins fiables) ⇒ au plafond, Final + note.
## 4. Lots B/F
Backend : **B1** domaine (variante+provider_key, VO HttpChatConfig, champ chat_http, port ToolInvoker/ToolSpec/ToolInvocationError ; tests sérialisation zéro-régression, round-trip, invariants ; zéro I/O). **B2** adapter infra openai_compat (reqwest/rustls, parse_* purs, mapping ReplyEvent+erreurs, résolution clé env ; tests fixtures OpenAI/Ollama, erreurs via wiremock, pas de fuite payload). **B3** boucle outils bornée + persistance/rechargement transcript run-dir (reprise) + dégradation tools non supporté (tests ToolInvoker fake, reprise=rechargement, plafond). **B4** routing (bras match) + composition root (with_tool_invoker, impl ToolInvoker→OrchestratorService) + profil de référence "Ollama / OpenAI-compatible local model" éditable (tests factory route, supports true, probe indispo→Start).
Frontend : **F1** types TS HttpChatConfig + champ chatHttp? + adapter "openAiCompatible". **F2** wizard/settings : sélectionnable comme Claude/Codex, form endpoint/model/apiKeyEnv/timeouts, validation miroir backend (first-run/profile.ts isValidEnvVar, URL, non-vide), apiKeyEnv = nom de var jamais clé. **F3** cellule = chemin chat structuré existant (dérivé de is_selectable, "gratuit" si DTO respecté) + erreur "endpoint indisponible" propre sans bloquer UI (tests RTL gateways mock).
Frontière B↔F : **DTO AgentProfile** (déjà le pont IPC) porte structuredAdapter:"openAiCompatible" + chatHttp{endpoint,model,apiKeyEnv?,requestTimeoutMs?,connectTimeoutMs?,maxToolIterations?}. Sélectionnabilité + rendu chat dérivés du DTO existant, pas de nouveau canal ni commande Tauri au cœur (le seed du profil de référence peut passer par le CRUD profils existant — à confirmer B4/F3).
## 5. Vigilance régression / invariants
1. Zéro régression sérialisation (skip_serializing_if=Option::is_none ; round-trip Claude/Codex bit-identique — BLOQUANT). 2. Isolation code : nouvel adapter = fichier neuf ; INTERDIT de toucher claude.rs/codex.rs/process.rs ; seul diff existant = 1 bras match factory.rs + champs optionnels domaine. 3. Contrat flux : exactement un Final terminal ; Heartbeat/ToolActivity/TextDelta/RateLimited non terminaux (conformance.rs doit couvrir le nouvel adapter). 4. Frontière domaine : reqwest UNIQUEMENT infra ; aucun type HTTP en domaine/application ; ToolInvoker port pur. 5. Nouvelle dépendance AppImage : reqwest rustls-tls (pas OpenSSL, spike §13-2). 6. Non-mélange ids : conversation_id()=None ; transcript clé-agent ; distinct de LS6. 7. Sandbox : pas de process spawné ⇒ SandboxEnforcer OS = no-op pour le tour (pas une régression) mais les outils via ToolInvoker gardent le gating orchestrateur.
## 6. Décisions produit remontées à Main (NON tranchées par Architect)
1. Surface d'outils v1 : parité totale immédiate vs sous-ensemble curaté (fiabilité tool-calling des modèles locaux). Reco : parité + garde-fou max_tool_iterations.
2. Streaming SSE dès v1 (reco, parité observabilité live) vs non-streaming (repli si endpoint ne stream pas).
3. Nom canonique variante : `OpenAiCompatible` (reco, décrit le protocole ; provider_key figé) vs `LocalChat` (formulation ticket).

View File

@ -0,0 +1,29 @@
---
name: ticket16-menus-floating-delivered-devfrontend
description: memory note ticket16-menus-floating-delivered-devfrontend
metadata:
type: project
---
# Lot A / #16 — MenuBar + FloatingWindow livré (DevFrontend)
Branche `feature/ui-menus-floating` (base develop 0218e32), non commité, avant merge Git. `vitest` **vert** : 54 fichiers / 527 tests. `tsc --noEmit` exit 0.
## UX pass validée utilisateur (barre unique + sélecteur projet) — APPLIQUÉE
- **Barre unique** : plus de MenuBar séparée dans App.tsx. Une seule MenuBar dans `ProjectsView` regroupant `[ Projet: <actif> ▾ ] File ▾ View ▾ Settings ▾`.
- **Sélecteur de projet permanent** : premier menu de la barre, label = projet actif, items = tous les projets connus (`vm.projects`) → `selectProject(id)` (active l'onglet si ouvert, sinon `openProject`). Accessible même sous projet actif, sans ouvrir File→Projects….
- **Settings → AI Profiles** déplacé dans la barre unique : toggle `showSettings` LOCAL à ProjectsView ; le main area swap vers `<ProfilesSettings/>` pendant que la barre reste visible (permet de refermer). App.tsx ne gère plus showSettings ni ProfilesSettings (juste firstRun ? wizard : ProjectsView).
## Fichiers (état final)
- **Créés (lot A initial)** : `shared/ui/FloatingWindow.tsx`, `shared/ui/MenuBar.tsx`, `shared/ui/FloatingWindow.test.tsx`.
- **Modifiés** : `shared/index.ts`, `shared/ui/zIndex.ts` (réutilisé), `app/App.tsx` (barre + showSettings + ProfilesSettings retirés), `features/projects/ProjectsView.tsx` (barre unique : projectMenu+File+View+Settings, showSettings local, ProfilesSettings dans main), `features/projects/projects.test.tsx` (+2 tests : sélecteur projet, toggle AI Profiles), `features/projects/ProjectsView.ls7.test.tsx`.
## Invariants conservés
- FloatingWindow (focus-trap+Échap+backdrop, z=floatingWindow 50), MenuBar dropdowns z=menuDropdown 40, toasts z=toast 70, zIndex canonique réutilisé.
- TOUS les panneaux atteignables via **View** (context, work, tickets, agents, templates, skills, perms, memory, git) + File→Projects…. Critère d'acceptation respecté.
- Non touché : `shouldShowWritePortalVeil`, exclusion write-portal↔F3, overlays in-cell, TicketPicker (#18).
## Notes
- ProfilesSettings rendu DANS le main de ProjectsView (barre + onglets projet restent visibles au-dessus) au lieu du swap plein-écran App précédent — meilleure continuité, barre unique toujours accessible.
- Pas de run live Tauri (couverture RTL mock-driven). DoD vitest satisfaite.
QA/utilisateur revalident avant merge Git.

View File

@ -0,0 +1,9 @@
---
name: ticket17-focus-trap-fix-merged-develop
description: Correctif de la perte de focus dans l'édition de ticket (#17) livré sur develop via feature branch dédiée.
metadata:
type: reference
---
Bug #17 : saisie une lettre à la fois dans le formulaire d'édition de ticket, cause = focus-trap `FloatingWindow` avec `useEffect` dépendant de `[onClose]` (closure recréée à chaque render). Fix DevFrontend : `onCloseRef` + effet focus-trap mount-only (`[]`).
Topologie Git : branche `feature/ticket17-focus-trap-fix` depuis `develop`, commit `4cccd40` (2 fichiers frontend seulement), merge `--no-ff``develop` = `2f8467e`, branche supprimée. Vert : tsc clean, 531/531 + test de régression. Voir [[ticket16-menus-floating-delivered-devfrontend]], [[ui-rework-sprint-scoping-contracts]].

View File

@ -0,0 +1,28 @@
---
name: ticket18-picker-delivered-devfrontend
description: memory note ticket18-picker-delivered-devfrontend
metadata:
type: project
---
# Lot B / #18 — TicketPicker livré (DevFrontend)
Branche `feature/ticket-picker-popup` (base develop d97abb1). `vitest` **vert** : 53 fichiers / 515 tests, dont 8 nouveaux (`TicketPicker.test.tsx`) + 29 `tickets.test.tsx` inchangés (valide la non-régression du listing après extraction). `tsc --noEmit` clean.
## Fichiers
- **Créés** : `shared/ui/zIndex.ts`, `features/tickets/TicketFacetsBar.tsx`, `features/tickets/useTicketSearch.ts`, `features/tickets/TicketPicker.tsx`, `features/tickets/TicketPicker.test.tsx`.
- **Modifiés** : `shared/index.ts` (export zIndex), `features/tickets/index.ts` (barrel), `features/tickets/TicketsPanel.tsx` (consomme `TicketFacetsBar`).
## Écarts de contrat à trancher (signalés à Main)
1. **Emplacement TicketPicker** : placé sous `features/tickets/`, PAS `shared/ui`, car il dépend du domaine ticket + gateways DI (shared/ui = design system pur). Conforme à tous les autres overlays (SprintManager, MemoryEditor…). Seul `zIndex.ts` (niveau design-system) est dans shared/ui.
2. **`TicketPickerResult.id`/`number`** : `ticket_list`/`TicketSummary` ne portent NI `id` NI `number`. Résolus par un `ticket_read` unique **à la sélection** (`useTicketSearch.resolve`). `number` serait dérivable du ref mais `id` exige la lecture. → contrat `{ref,id,number,title,status,priority}` respecté sans nouveau endpoint. Écart backend candidat : ajouter id/number au summary (dette basse).
3. **`zIndex.ts` provisoire** : lot A (#16) introduit le canonique en parallèle. Valeurs alignées sur l'échelle figée (menuDropdown=40, floatingWindow=50, floatingWindowNested=60, toast=70). **À réconcilier au merge** (garder un seul module). Picker monté à `floatingWindowNested=60`.
## Notes d'impl
- G3-bis respecté : `cursor` traité en token opaque (jamais construit/incrémenté ; relayé verbatim via `loadMore`).
- `excludeRefs` filtré client-side après réception de page.
- `useTicketSearch` léger (pas de groupement sprint, pas de souscription events, debounce 200ms) — ne réutilise pas `useTickets`.
- `TicketFacetsBar` = recherche + facettes statut/priorité (labels aria préservés : `search tickets`, `filter status …`, `filter priority …`, `clear filters`). Le select assignee reste dans `TicketsPanel` (hors périmètre picker).
- Focus-trap inline (Tab cycling + Escape + restore focus) car aucun util partagé n'existe ; G5 respecté (montage niveau chrome par le consommateur #17/#19).
- `selectionMode:"multi"` câblé de façon extensible (footer réservé) mais non actif en V1.
QA repasse derrière. Consommateurs #17 (link) et #19 (sprint) importent `TicketPicker` depuis `@/features/tickets`.

View File

@ -0,0 +1,11 @@
---
name: ticket25-assistant-sandbox-eacces-rootcause
description: Root cause et contrat de fix du EACCES (os error 13) au spawn de la CLI d'un assistant de ticket — le preset SandboxPlan::project_read_only handle la classe exec Landlock.
metadata:
type: reference
---
**Symptôme** : ouvrir un assistant de ticket (Codex ou Claude), premier message ⇒ `process error: agent session start failed: <cli>: Permission non accordée (os error 13)`. Indépendant de la CLI.
**Root cause** : `OpenTicketAssistant::execute` (`application/src/ticket_assistant.rs:97`) fabrique un plan bespoke via `SandboxPlan::project_read_only` (`domain/src/sandbox.rs:153`) = grant **RO** sur `project_root` + RW sur le run_dir, posture `Deny`. Dans l'adapter Landlock (`infrastructure/src/sandbox/landlock.rs:70`), un grant RO *handle* la classe read, et `AccessFs::from_read(V1)` **inclut `AccessFs::Execute`**. Le binaire CLI vit hors du project root ⇒ `execve` refusé ⇒ EACCES au `spawn()` sandboxé (`process.rs:248`). Le chemin workspace n'a pas le bug car son plan vient de `compile_sandbox_plan` sous posture `Allow`/write-only ⇒ l'adapter no-op ou ne handle que write, read/exec restent ouverts. Piège déjà documenté dans l'adapter (« enrichment is the launch-path's concern LP4-2 »).
**Fix (backend-pur, zéro port/DTO)** : remplacer le preset par un **write-fence** — ne jamais handle read/exec (pas de grant RO), handle write seul, RW-grant = run_dir + state/home CLI + temp, posture Deny ⇒ écriture projet refusée, binaire/libs/home lisibles-exécutables. Fallback sûr : passer `None` à `factory.start` pour la surface ticket (garde policy MCP + projection advisory). Garde-fous fonctionnels réels = policy MCP `idea_ticket_read/update*` + LP3, pas l'OS-sandbox. Lié à [[ui-rework-sprint-scoping-contracts]].

View File

@ -0,0 +1,17 @@
---
name: ticket28-f1-frontend-delivered
description: Lot F1 du ticket #28 livré sur feature/ticket28-firstrun-detect-hang — invariant busy/detectProfiles, flag detecting, timeout UI, test de non-régression.
metadata:
type: reference
---
Lot **F1** (frontend) du bug #28 implémenté sur `feature/ticket28-firstrun-detect-hang` (non committé, Git s'en charge).
**Invariant posé** : dans `frontend/src/features/first-run/useFirstRun.ts`, le retour de `busy` à `false` ne dépend d'aucun appel à `detectProfiles`. `busy` n'entoure plus que `firstRunState()` (reload) et `configureProfiles()` (finish). Toute future régression qui remet un `await profile.detectProfiles(...)` sur un chemin gouvernant `busy` re-gèle le wizard.
**Mécanisme** : `runDetection(candidates, { preselect, reportError })` factorise auto-détection et bouton manuel. Le flag `detecting` est relâché par un `setTimeout(DETECT_TIMEOUT_MS = 3_000)`, **jamais** par la promesse — car le `finally` d'un `await` sur une promesse jamais settled (panic Rust ⇒ pas de réponse IPC) n'est jamais atteint. Un compteur `detectRun` invalide les rounds périmés (re-clic, unmount).
**Piège UI à retenir** : `<Button loading>` implique `disabled` dans le design system (`shared/ui/Button.tsx:46`). Donc `detecting` ne peut pas être surfacé via `loading` sur les boutons Save/Detect sans réintroduire le grisage — il est rendu comme un `role="status"` « Detecting… » séparé.
**Point de vérité QA** : `FirstRunWizard.test.tsx`, describe « detection that never answers (ticket #28) », gateway `HangingDetectGateway` dont `detectProfiles` renvoie `new Promise(() => {})`. Vérifié à teeth : en réintroduisant `setBusy(true); await runDetection(...)` dans `reload`, 2 des 3 tests échouent avec exactement le symptôme du ticket.
Vert : `npx vitest run` 59 fichiers / 569 tests, `npx tsc --noEmit` exit 0. Pas de lint dans ce projet (aucun eslint config ni script). F1 débloque l'UI mais **B1 (DevBackend, `AgentRuntime::detect` sync→async) reste requis** pour que la détection remonte réellement un résultat.

View File

@ -0,0 +1,19 @@
---
name: ticket28-firstrun-detect-hang-scoping
description: Cadrage hexagonal du bug bloquant first-run (#28) — panic block_on imbriqué dans detect + découplage busy/détection, contrats et point de vérité QA.
metadata:
type: reference
---
Bug high #28 (build 0.3.0 / develop 3c6cd04) : au first-run, « Save and continue » ET « Detect installed CLIs » restent grisés → wizard impossible à terminer.
**Cause racine unique (bloquante) — CONFIRMÉE source :** `AgentRuntime::detect` est resté `fn` synchrone (`domain/src/ports.rs:754`, impl `infrastructure/src/runtime/mod.rs:182`) alors que le trait est déjà `#[async_trait]`. L'impl bridge le spawner async via `futures_block_on`/`block_on` (mod.rs:228-237), appelé DANS la commande async Tauri `detect_profiles``DetectProfiles::execute` (async) → `detect()` sync sur la même task → tokio panic « Cannot start a runtime from within a runtime ». Un panic dans une commande async Tauri = **aucune réponse IPC** → promesse `invoke` jamais résolue/rejetée → le try/catch interne (`useFirstRun.ts:79-90`, n'attrape que les rejets) ne l'attrape pas → `finally` jamais atteint → `busy` figé true → boutons `disabled: vm.busy` grisés à vie. Se déclenche au 1er profil sondé, toute machine. Déclencheur nouveau de #14 : auto-détection à l'ouverture dans `reload`.
**Cause secondaire (NON bloquante) :** profil `ollama-openai-compatible` (command `openai-compatible`, `detect:None`) → fallback `openai-compatible --version` (binaire absent) → `available:false` erroné (spawn échoue vite, pas de hang). Endpoint HTTP sondé comme un CLI = catégorie-fausse. base_url dispo via `HttpChatConfig` (http://localhost:11434/v1). Précédent réutilisable : `infrastructure/src/store/embedder.rs:401 detect_ollama` (reqwest, timeout 800ms, feature vector-http).
**Fix figé — Backend (DevBackend) :** B1 promouvoir `detect` en `async fn` (supprimer futures_block_on entièrement), maj `DetectProfiles::execute` (.await) + commande. B2 `tokio::time::timeout` borné par probe. B3 brancher endpoint-kind (chat_http / StructuredAdapter::OpenAiCompatible) → probe HTTP best-effort borné de base_url (jamais d'erreur dure) ; CLI-kind = `detection_spec` INCHANGÉ (zéro-régression Claude/Codex). Aucun nouveau port/adapter.
**Fix figé — Frontend (DevFrontend) :** F1 découpler `busy` de la détection : `busy` n'entoure que `firstRunState()`, relâché dès rendu des lignes. Auto-détection = étape détachée non bloquante suivie par un flag distinct `detecting` (ne grise pas Save), avec timeout UI (~3s). Invariant : retour de busy à false ne dépend JAMAIS de detectProfiles.
**Contrats :** port `AgentRuntime::detect` sync→async (validé Architecture). DTO detect_profiles INCHANGÉ (available:bool). Catalogue ollama : detect reste None, détection décidée par nature endpoint.
**Point de vérité QA :** Backend = test `#[tokio::test]` multi-thread sur DetectProfiles::execute (échoue au panic sur code actuel, passe après B1) + endpoint sans spawn CLI + CLI Claude/Codex inchangé. Frontend = RTL avec mock detectProfiles jamais résolu → Save+Detect actifs dès firstRunState. Live = first-run machine sans Claude/Codex, Ollama coché, boutons actifs, Save termine (rebuild AppImage obligatoire). F1 seul débloque déjà ; B1 supprime la cause racine ; les deux requis pour vert.

View File

@ -0,0 +1,18 @@
---
name: ticket4-announcements-frontend-f1f2f3
description: Topologie du frontend des annonces inter-agent (store borné, preview demandeur filtré requester, overlay cible piloté par le busy/idle par agent) — réintégré sur base develop.
metadata:
type: project
---
Branche `feature/agent-live-announcements` (base develop `ebd992e`, non commité). Frontend réintégré depuis `backup/announcements-work-20260704`. Feature `frontend/src/features/announcements/` :
- `announcementsStore.ts` : index pur `byTarget[target][ticketId]->Announcement[]`, `appendAnnouncement` borné (cap 50), `purgeAllForTarget` (au busy:false), sélecteurs target/requester. Éphémère, jamais persisté. (`purgeTurn` du backup RETIRÉ — plus de signal final sur develop.)
- `AnnouncementsProvider.tsx` (monté dans App.tsx) : 1 abonnement `system.onDomainEvent`. Deux signaux : CONTENU = `agentAnnouncement`→append ; CYCLE DE VIE = map `busy` par agent, seule autorité de montage/retrait, alimentée par `agentBusyChanged{agentId,busy}` + hydratée du read-model `ProjectWorkState.agents[].busy` via `useHydrateAgentBusy(projectId,agentId)` (seed « si absent » → event live gagne). `busy:false`→purgeAllForTarget. Hooks `useTargetAnnouncements` (active=busy[target]), `useRequesterAnnouncements`, `useHydrateAgentBusy`. Hors provider = store inerte.
- F3 `TargetAnnouncementsOverlay(projectId,agentId)` monté dans **`LayoutGrid` LeafView** (PAS ConversationCell — absent sur develop ; la cellule agent est le PTY `TerminalView`). Overlay `pointer-events-none`, z-20, au-dessus du write-portal overlay existant. F2 `AnnouncementsPreview` par ligne d'`AgentsPanel` (requester==a.id).
ADAPTATION develop appliquée : `agentTurnEvent{final}` N'EXISTE PAS sur develop → branche de purge retirée du provider, type NON réintroduit dans domain, 2 tests agentTurnEvent supprimés. Autorité unique F3 = busy. Confirmé présents sur develop : `agentBusyChanged{agentId,busy}` (domain/index.ts) et `AgentWorkState.busy: {state:"idle"}|{state:"busy";ticket;sinceMs}`. DTO annonce backend confirmé (events.rs, test JSON à events.rs:927) : `{type:"agentAnnouncement",projectId,requester("user"|agentId),target,ticketId,text,atMs}` — mirroir ajouté dans domain/index.ts.
Exclusion des voiles (arbitrage [[ticket4-overlay-composition-leafview]]) : le voile write-portal (injection PTY) et l'overlay F3 partagent le conteneur relatif du LeafView. Prédicat pur exporté `shouldShowWritePortalVeil(agentPinned, writePortalOverlay, busyActive) = agentPinned && overlay && !busyActive` ; F3 se gate sur le même `busyActive` (`useTargetAnnouncements(agentId).active`, remonté au LeafView). Résultat : exactement UN voile, F3 prioritaire. Purement visuel — `portal.isSuspended()`/l'injection PTY (TerminalView) INCHANGÉS. Testé exhaustivement (table de vérité) dans `overlayExclusion.test.ts`.
Écarts tranchés à signaler : (1) F3 hébergé dans la cellule PTY (LeafView) faute de ConversationCell ; (2) busy est l'autorité brute : sur develop busy vient du mediator (délégations/inter-agent), pas du typing PTF humain, donc proxy correct, mais surveiller un éventuel over-trigger ; (3) F2 filtre requester seul (pas de ticketId par ligne) = garde anti-fuite.
Build vert, 51 fichiers / 481 tests vitest verts.

View File

@ -0,0 +1,25 @@
---
name: ticket4-overlay-composition-leafview
description: Règle de composition des deux voiles plein-cellule de LeafView (write-portal injection PTY vs F3 annonces busy) — exclusion mutuelle avec priorité F3, arbitrée pour éviter le double voile.
metadata:
type: reference
---
Arbitrage layout Architect 2026-07-04 (branche feature/agent-live-announcements, base develop ebd992e, 481 tests verts).
**Problème vérifié :** LeafView héberge DEUX voiles plein-cellule inset:0 dans le même conteneur relatif :
- write-portal (LayoutGrid.tsx:841, zIndex:4, rgba(0,0,0,0.45), « Un agent est en train de parler… ») = délégation injectée dans le PTY de l'agent (§20.3, flag `overlay` de useWritePortal).
- F3 TargetAnnouncementsOverlay (z-20, bg-canvas/85, « …en train de LUI parler… » + réflexions) = agent busy (agentBusyChanged mediator).
Ils rendent le MÊME événement depuis deux signaux et montent ENSEMBLE sur le chemin d'injection inter-agent (overlay+busy) → double voile empilé + bannières dupliquées = rendu cassé, chemin nominal.
**Décision (DevFrontend, layout, zéro backend) :** exclusion mutuelle, un seul voile à la fois, priorité F3 :
- busy(agent) → F3 seul ; voile write-portal gated `agentId!=null && overlay && !busyActive`.
- sinon overlay → write-portal seul (repli fenêtre sans busy).
- jamais les deux.
Sécurité : on ne touche que le visuel ; la suspension des frappes (portal.isSuspended(), TerminalView.tsx:182) reste ; F3 est pointer-events-none.
**Couplage point 3 :** F3 DOIT rendre la bannière sur busy même à 0 annonce (déjà: if(!active) return null, TargetAnnouncementsOverlay.tsx:56), car F3 absorbe le voile write-portal — une garde « ≥1 annonce » rouvrirait le trou d'indication. Pas de garde.
**F2 (point 4) :** filtre requester==id de ligne suffit en V1 (isolation fil partagé) ; ticketId optionnel dormant.
**F3 hébergement (point 1) :** conteneur LeafView validé (pas de ConversationCell sur develop).
Liens : [[ticket4-restart-on-develop-ebd992e]], [[ticket4-f3-overlay-driven-by-agentbusychanged]], [[inter-agent-live-context-shared-per-agent]].

View File

@ -0,0 +1,20 @@
---
name: ticket4-restart-on-develop-ebd992e
description: Cadrage du redémarrage des annonces inter-agent sur la base pré-canonique develop ebd992e — ce qui existe, l'écart live, la réutilisabilité du backup et le découpage B/F.
metadata:
type: reference
---
Redémarrage #4 sur base réalignée main=develop=feature/background=feature/agent-live-announcements=ebd992e (pré-canonique). Vérifié sur le dépôt le 2026-07-04.
**Signaux sur develop :**
- Émission intermédiaire : AUCUNE. codex.rs draine via run_turn BATCH (process.rs:268/321 lit ligne-à-ligne mais collecte en Vec, renvoie à EOF ; codex.rs:260 Box::new(events.into_iter())). structured.rs:266-271 jette TextDelta/ToolActivity/Heartbeat, ne renvoie que Final.
- Busy PAR AGENT : PRÉSENT ET COMPLET. DomainEvent::AgentBusyChanged{agent_id,busy} (domain/events.rs:171), autorité mediator (infrastructure/input/mod.rs:433), relay (app-tauri/events.rs:484), front event domain/index.ts:22 + read-model agents[].busy {state,ticket,sinceMs} (:220,304) réconcilié reboot. ⇒ autorité overlay F3 dispo telle quelle.
- Fix Final : PRÉSENT (62915ee) mais dégrade les agent_message intermédiaires en ToolActivity{label} en JETANT leur texte, et reste batch → inexploitable pour annonces.
**Écart live (faisable SANS moteur canonique) :** run_turn lit déjà incrémentalement ; le batch n'est qu'un artefact de sa signature ->Vec. Ajouter un tap Option<mpsc::Sender<String>> dans run_turn/drain/drain_sandboxed (boucle existante) ; l'orchestrateur (qui détient requester/target/ticket) publie DomainEvent::AgentAnnouncement au fil de l'eau. Thread Landlock n'envoie que des lignes brutes ; publication bus hors thread. Pas de spawn_turn/AgentTurnEvent/ConversationId.
**Réutilisabilité backup (backup/announcements-work-20260704, 691b2ce) :** frontend features/announcements/ (store borné, provider, AnnouncementsPreview=F2, TargetAnnouncementsOverlay=F3) réutilisable ; F3 sur busy = intact sur develop. Dépend d'agentAnnouncement{requester,target,ticketId,text,atMs} (absent→B) et agentTurnEvent{final} (absent, secondaire → RETIRER du store). Backend backup non réutilisable (canonique) ; contrat/attribution repris.
**Découpage :** B1 domaine/appli (ReplyEvent::Announcement + DomainEvent::AgentAnnouncement + publish au drain) ; B2 infra = CŒUR (tap mpsc process.rs + codex émet Announcement{text} live) ; B3 relay/DTO ; F1 store (hook final) ; F2 preview (filtre ticketId+requester==self) ; F3 overlay (réutilisé, busy seul autorité).
Liens : [[ticket4-f3-overlay-driven-by-agentbusychanged]], [[ticket4-announcements-reintegration-topology]], [[inter-agent-announcements-feature-and-codex-final-bug]], [[inter-agent-live-context-shared-per-agent]].

View File

@ -0,0 +1,16 @@
---
name: ticket7-f2-delegated-limit-no-agentratelimited
description: Écart backend ticket #7/F2 — le rendez-vous délégué n'émet pas AgentRateLimited ni n'arme la reprise pour la cible limitée.
metadata:
type: project
---
Ticket #7, volet inter-agent. Constat F2 (frontend DevFrontend) sur `feature/session-limit-interagent`.
Le chemin délégué (rendez-vous headless) **n'appelle jamais** `session_limit_service.on_rate_limited(target, …)`. Dans `crates/application/src/orchestrator/service.rs:1876-1915`, la branche `TargetRateLimited` publie **uniquement** `DomainEvent::DelegationRateLimited{requester, target, resets_at_ms}` puis retourne `AppError::TargetRateLimited`. Les deux seuls sites de `on_rate_limited` sont les chemins humains PTY/chat (`crates/app-tauri/src/commands.rs:1409` et `:1535`).
**Conséquence** : quand la cible B est limitée **via une délégation**, aucun `AgentRateLimited`/`AgentResumeScheduled` n'est émis pour B ⇒ le badge mono-agent de B (piloté par `limitByAgent` dans `useAgents`) ne s'affiche pas et aucune reprise auto n'est armée pour B. Contredit le commentaire de `crates/app-tauri/src/events.rs:370-372` (« the target resume flow is surfaced by the existing agent limit events »).
**Why:** F2 supposait « même flux AgentRateLimited ». Le rendu frontend du badge est sain (re-render + bouton Annuler OK) ; l'écart est backend.
**How to apply:** correction = backend (appeler `on_rate_limited` pour la cible dans la branche rendez-vous, avec node/conversation adéquats). Ne PAS dériver le badge de B depuis `delegationRateLimited` côté front : cet event ne porte ni sémantique de reprise armée ni node_id/conversation_id pour armer le réveil → inventerait un contrat. Le côté DEMANDEUR (A) est, lui, bien couvert par F1 (`suspendedDelegationsByRequester`). Voir [[session-limit-handling-design]].

View File

@ -0,0 +1,15 @@
---
name: tickets-frontend-v1-done
description: État du frontend du système de tickets — livré, vert, décisions de contrat clés.
metadata:
type: project
---
Frontend V1 du système de tickets livré sur `feature/issue-ticket-system` (non commité, attend QA/Git). Build + 459 tests verts.
Décisions figées :
- Port nommé `TicketGateway` (pas IssueGateway) — aligné sur le wire `ticket_*`/`TicketDto` et le terme visible « ticket ».
- Statut/priorité côté UI passent par `ticket_update` : `ticket_update_status`/`ticket_update_priority` ne sont PAS des commands Tauri (MCP-only, non enregistrées dans `lib.rs`).
- Conflit de version = `code:"INVALID"` + message contenant `"issue version conflict"` (détection par message, pas de code dédié). Voir `isTicketVersionConflict`.
- F7 = `#ref` cliquables + linkification desc + bouton clipboard « Travaille sur le ticket #N : <titre> » (pas d'écriture terminal directe en V1).
Surface : `features/tickets/` (panel liste + overlay détail/carnet/liens/assign), onglet sidebar « Tickets » dans ProjectsView entre Work et Agents, `MockTicketGateway` émet les events `Issue*`. Voir [[checkpoint-issue-ticket-backend-v1-done]] et [[issue-ticket-system-design]].

View File

@ -0,0 +1,15 @@
---
name: tickets-t3-frontend-validation-verdict
description: Verdict de validation frontend du fix T3 (projection backgroundTasks par agent) sur feature/background-tasks-first-class — vert, avec 3 désalignements de contrat mineurs.
metadata:
type: reference
---
Validation frontend du fix T3 (ticket #1), branche `feature/background-tasks-first-class`, 2026-07-03.
**Verdict : vert.** `npx vitest run` = 460/460, `npm run build` (tsc --noEmit + vite) OK. Le front était déjà câblé pour Cancel/Retry ; contre le vrai payload backend le mapping d'états et l'activation des boutons fonctionnent.
**Contrat backend réel** (`crates/app-tauri/src/dto.rs::AgentBackgroundTaskStateDto`, camelCase) : `taskId, kind, state, exitCode?, summary?, stdoutTail?, stderrTail?, createdAtMs, updatedAtMs`. `state` ∈ queued|running|waiting|completed|failed|cancelled|expired ; `kind` ∈ command|headlessRendezvous|sessionResume|maintenance. **Pas** de `status`, `ownerAgentId`, `projectId`, ni `finishedAtMs`.
**Fait :** FE-1 rendu explicite `queued`/`waiting``pending` dans `normalizeBackgroundStatus`. FE-2 ajout d'un test sur le vrai shape dans `src/features/workstate/workstate.test.tsx` (le mock passe par le vrai `normalizeProjectWorkState`, donc pas de faux-vert possible).
**Écarts de contrat à arbitrer (dette, panneau non modifié) :** (1) `ProjectWorkStatePanel.tsx` trie sur `finishedAtMs` jamais émis → tri retombe sur taskId, non chronologique ; (2) `summary` backend non porté par `BackgroundCompletion` → ignoré ; (3) `ownerAgentId`/`projectId` absents du payload par-agent (inoffensif : fallback = agentId). Voir [[workstate-background-tasks-projection-fix]] et [[b8-in-app-trigger-run-in-background]].

View File

@ -0,0 +1,15 @@
---
name: tickets-v1-e2e-validated-qa
description: Verdict QA du système de tickets V1 sans attachments sur feature/issue-ticket-system — vert de bout en bout, avec la seule réserve du typage de conflit côté UI.
metadata:
type: reference
---
Validation QA de bout en bout du système de tickets **V1 (sans attachments)** sur `feature/issue-ticket-system` (backend commité `8de7be0`, frontend non commité). **Verdict : OK.**
**Preuves réelles :**
- `cargo build --workspace` OK. Tests verts sur domain/application ; infrastructure et app-tauri verts **sauf la dette pré-existante déclarée** (`orchestrator_watcher::ask_request_surfaces_reply_alongside_detail` + `app-tauri state::mcp_e2e_loopback_tests::*`, tous `Elapsed(())` du rendez-vous, sans rapport avec les tickets). Aucun échec imputable aux tickets.
- Frontend : `npm run build` OK (tsc+vite), `npm test` = **459 passed / 49 files**.
**Invariants prouvés par tests :** `#N` séquentiel jamais réutilisé (`allocator_never_reuses_numbers`) ; create→read round-trip ; carnet remplaçable & scoped `.ideai/tickets/N/carnet.md` ; pas de self-link (`IssueError::SelfLink`, `domain/issue.rs`) ; assignation agent connu seulement (`create_issue_rejects_unknown_assignee`) ; concurrence optimiste (`update_issue_maps_version_conflict`, `IssueStoreError::VersionConflict`) ; énums fermés statut/priorité.
**Réserve non bloquante (dette technique) :** le conflit de version est détecté côté **front** par fragment de message (`message.includes("version conflict")`, `frontend/src/adapters/ticket.ts`, `useTicketDetail.ts`), s'appuyant sur `domain/ports.rs` `#[error("issue version conflict: …")]`. Un code typé `"versionConflict"` existe DÉJÀ mais uniquement sur la surface MCP (`app-tauri/src/tickets.rs:942`) ; le chemin Tauri command renvoie `INVALID`. Recommandation : propager le code typé dans l'enveloppe d'erreur des `ticket_*` commands et brancher l'UI sur `error.code`. Comportement actuel correct et testé, donc non bloquant.

View File

@ -0,0 +1,19 @@
---
name: ui-rework-sprint-delivered-tickets-21-22-23
description: memory note ui-rework-sprint-delivered-tickets-21-22-23
metadata:
type: project
---
Sprint « UI rework » (order 1) bouclé le 2026-07-07. Les 4 premiers tickets (#16/#17/#18/#19) étaient déjà mergés ; les 3 derniers viennent d'être produits et intégrés. Cadrage : [[ui-rework-sprint-tickets-21-22-23-scoping]].
## Livré (tous verts QA par exécution réelle, mergés --no-ff sur develop)
- **#21** tri des tickets (B+F) — merge `37bf7d4`. Backend : `TicketListQuery.sort?:{field:"number"|"priority"|"status"|"title";direction:"asc"|"desc"}`, appliqué avant pagination, tie-break systématique par `number` croissant. Ordres sémantiques : priority low<medium<high<critical ; status open<inProgress<QA<closed ; title casse-insensible. `sort` absent = comportement historique. Frontend : contrôle « Trier par » dans `TicketFacetsBar` (partagé TicketsPanel + TicketPicker), `sort` dans query+queryKey (reset pagination), curseur reste opaque (G3-bis).
- **#22** docking de views (F pur) merge `2323a71`. Nouvelle primitive `shared/ui/DockRegion` + modèle `features/projects/viewPlacement.ts` : `ViewPlacement = "closed"|"floating"|{dock:"left"|"right"}` par PanelId, invariant un-seul-slot. Ne réutilise NI FloatingWindow NI LayoutTree terminaux (monté chrome-level ProjectsView).
- **#23** fenêtres système (B+F) merge `47b1d64` (HEAD develop). Backend app-tauri : commands `open_view_window({panel,projectId})→{label,url,alreadyOpen}` et `close_view_window(...)→{label,closed}`, vraie WebviewWindow chargeant `index.html?panel=&project=`, registre anti-doublon (label `view-{panel}-{projectIdSansTirets}`), event unique `view-window://lifecycle` payload `{kind:"opened"|"focused"|"closed",panel,projectId,label}`, capability étendue à `view-*`. Frontend : port `WindowGateway` (adapter Tauri + mock), entrée panel-only `app/ViewWindow.tsx` routée dans `main.tsx`, `ViewPanelBody` partagé, `ViewPlacement` étendu à `"detached"`, menu Window + bouton ⤢, garde no-direct-invoke.
## Dette basse restée ouverte (hors périmètre, non bloquante)
- Curseur `ticket_list` toujours offset non opaque/stable (cf. [[ui-rework-sprint-scoping-contracts]]) non soldée par #21.
- Persistance restore-au-redémarrage du docking #22 et du placement détaché #23 : follow-up backend différé (V1 = placement éphémère useState).
## État Git
develop @ `47b1d64` ; branches feature/ticket21-22-23 supprimées (mergées). Rien de sortant, `main` intact. Release `develop→main` en attente de validation utilisateur.

View File

@ -0,0 +1,23 @@
---
name: ui-rework-sprint-scoping-contracts
description: Frontières hexagonales et contrats figés du sprint UI rework — les 4 tickets sont frontend-purs, la popup TicketPicker #18, l'échelle z-index et le curseur opaque.
metadata:
type: reference
---
Sprint « UI rework » (order 1), cadré 2026-07-06. Les **4 tickets sont frontend-purs** : aucun nouveau port/read-model/endpoint.
**Fait clé** : filtrage/recherche tickets déjà backend-driven via `TicketGateway.list(projectId, TicketListQuery{statuses[],priorities[],assignedAgentId,text,limit,cursor})` (ports/index.ts:717) → adapter transmet à `ticket_list`. Client ne fait que le groupement par sprint. #18 réutilise ce contrat, pas d'endpoint dédié.
**G4 (contrat ticket_list) levé pour B** après revue DevBackend : statuses/priorities OK (OR intra / AND inter). Deux écarts en DETTE BASSE : (a) `text` ne matche pas `#ref` (tickets.rs:681, issues.rs:322/326/465) ; (b) `cursor` = offset numérique non opaque/non stable inter-pages (tickets.rs:1231). Acceptable pour picker single-select court.
**G3-bis (garde non-breaking)** : TicketPicker/useTicketSearch traitent `cursor` comme token OPAQUE — ne jamais le construire/incrémenter, ne relayer que le cursor de la page précédente. Migration curseur opaque backend = zéro changement frontend.
**Base de code** : pas de routeur ni store global (navigation = useState local App.tsx + ProjectsView.tsx, sidebar inline ProjectsView.tsx:224). Aucune primitive Modal/Portal partagée ; ~7 features hand-roll `fixed inset-0 role=dialog` z-50/z-[60]. Pas de createPortal. #16 introduit `shared/ui/FloatingWindow` (focus-trap, monté niveau chrome) + `shared/ui/zIndex.ts`.
**Contrat #18 figé**`TicketPicker` props `{open, projectId, title?, confirmLabel?, selectionMode?:"single"|"multi" (défaut single), excludeRefs?: TicketRef[] (filtré client-side), initialQuery?: TicketListQuery, onSelect(TicketPickerResult|[]), onClose}`. `TicketPickerResult={ref,id,number,title,status,priority}`. Extraire `TicketFacetsBar` (fieldsets statut/priorité + recherche depuis TicketsPanel.tsx) + hook `useTicketSearch` (ne PAS réutiliser useTickets, trop lourd). #17 et #19 consomment en single.
**Échelle z-index figée (G2)** : in-cell inchangé (chrome 2-5, F3 z-20, write-portal z-4, resume z-5) ; chrome full-window menuDropdown=40, floatingWindow=50, floatingWindowNested=60, toast=70.
**Règle intégration (G5)** : fenêtres flottantes/menus montées niveau chrome (ProjectsView/App), JAMAIS dans LayoutGrid/LeafView. Ne pas toucher shouldShowWritePortalVeil ni exclusion write-portal↔F3 (cf. [[ticket4-overlay-composition-leafview]]). Focus-trap obligatoire (xterm garde le focus clavier).
**Ordre** : A(#16) ∥ B(#18) racines → prioriser B (chemin critique #17+#19) ; D(#19) après B ; C(#17) après A+B. Tout DevFrontend ; DevBackend = ticket dette basse (matching #ref + curseur opaque/stable).

View File

@ -0,0 +1,39 @@
---
name: ui-rework-sprint-tickets-21-22-23-scoping
description: memory note ui-rework-sprint-tickets-21-22-23-scoping
metadata:
type: project
---
Cadrage hexagonal des 3 derniers tickets du sprint « UI rework » (order 1), fait 2026-07-07 par Architect. Les 4 premiers (#16/#17/#18/#19) sont livrés/mergés sur develop — cf. [[ui-rework-sprint-scoping-contracts]]. Ne pas re-cadrer ; produire.
## État de départ vérifié
- Vues (context/work/tickets/agents/templates/skills/permissions/memory/git) = composants React rendus via menu **View**, pilotés par `openPanel: useState<PanelId|null>` (ProjectsView.tsx:127). UNE seule vue à la fois dans un `FloatingWindow` modal centré (ProjectsView.tsx:497).
- `FloatingWindow` = overlay MODAL (fixed inset-0, backdrop, focus-trap, z-50), non déplaçable/non redimensionnable, purement présentationnel.
- LayoutTree terminaux (cellules/LeafView, layout.json via ProjectStore) = domaine DISTINCT ; ne pas confondre avec le placement des vues.
- `TicketListQuery` (ports/index.ts:717) N'A PAS de sort/order. Pagination RÉELLE (useTicketSearch + loadMore, TicketsPanel.tsx:225 charge 100 puis +100).
## #21 tri tickets (low) — NON frontend-pur : B + F
Le tri est une préoccupation de QUERY, symétrique du filtrage déjà backend-driven (TicketGateway.list→ticket_list). Comme la liste est paginée, trier client ne trie que les pages chargées → FAUX. Refuser le « frontend-pur ».
- **B** : étendre `TicketListQuery`/`ticket_list` avec `sort?:{field:"number"|"priority"|"status"|"title"; direction:"asc"|"desc"}`. ORDRE TOTAL déterministe obligatoire (tie-break par number croissant, sinon curseur casse). priority/status = ordre sémantique enum explicite, pas alpha. Défaut absent = comportement actuel (champ optionnel, rétro-compat). Companion recommandé non imposé : solder la dette basse curseur offset non opaque/stable (cf. [[ui-rework-sprint-scoping-contracts]]) — peut rester ticket séparé.
- **F** : contrôle « Trier par… » dans `TicketFacetsBar` (partagé TicketsPanel + TicketPicker #18). `useTicketSearch` relaie `sort` dans la query, `sort` dans la queryKey (reset pagination). Ne pas toucher le traitement OPAQUE du curseur (G3-bis). Groupement-par-sprint reste client ; tri s'applique intra-groupe.
- Dépend #16/#18 (livrés). Indépendant de #22/#23.
## #22 Anchor/docking views (medium) — frontend-pur V1 ; persistance = B optionnel différé
NE PAS réutiliser FloatingWindow (overlay modal) ni LayoutTree terminaux. NOUVELLE primitive de docking chrome-level, en-flux (pas overlay).
- Frontière : docking = état présentation chrome-level machine-local, distinct du domaine LayoutTree (réservé cellules PTY). Jamais dans LayoutGrid/LeafView (G5).
- Généraliser `openPanel:PanelId|null` en modèle de placement : `ViewPlacement = "closed"|"floating"|{dock:"left"|"right"}` par PanelId — conçu EXTENSIBLE pour #23 (ajoutera "detached"). Invariant : une vue = exactement un slot. V1 reco : 1 vue ancrée par côté, largeur redimensionnable.
- **F** : `shared/ui/DockRegion` (colonne verticale resizable gauche/droite du <main>), header titre + bascule flottant↔ancré. ProjectsView remplace openPanel par le placement. Réutilise composants de vue existants tels quels.
- **B optionnel différé** : persistance restore-au-redémarrage = préférence UI machine-local → store global settings.json/workspace.json (archi §9.2 via ProjectStore), PAS layout.json projet. V1 éphémère (useState) ; persistance = follow-up ticket dédié.
- Autonome. FOURNIT le modèle de placement dont #23 dépend.
## #23 views en fenêtres système (medium) — impact backend Rust + composition root
Cas spécialisé du chantier L10 multi-fenêtre (Tauri v2 WebviewWindow, protocole detach, archi §13.3). Avantage vs detach onglet-terminal : vues gateway/read-model-driven, PAS liées à un Channel PTY → nouvelle WebviewWindow ré-instancie le DI et re-souscrit aux gateways, AUCUN transfert d'état lourd.
- **B (app-tauri)** : command `open_view_window({panel,projectId})` créant une WebviewWindow sur entrée dédiée (URL param ?panel=&project=), lifecycle (focus/close/registre anti-doublon), events cycle de vie (fermeture OS → re-bascule placement). Composition root (di.tsx) : supporter un shell « panneau seul » (mêmes adapters/gateways).
- **F** : nouveau port `WindowGateway.openViewWindow(panel,projectId)` (+ closeViewWindow) avec adapter Tauri + mock (jamais invoke direct). Entrée panel-only lisant panel/project depuis URL. Menu View : action « Ouvrir dans une nouvelle fenêtre ».
- Articulation #22 FIGÉE : `ViewPlacement = "closed"|"floating"|{dock:"left"|"right"}|"detached"`. Une vue = soit ancrée soit flottante soit détachée, jamais deux (même invariant un-seul-slot). #23 = ajoute "detached" + plomberie Tauri.
- DÉPEND du modèle de placement de #22.
## Ordre & dépendances
- #21 en parallèle, autonome (petit B+F, le plus rapide, chaîne tickets uniquement).
- #22 AVANT #23 : #22 pose ViewPlacement que #23 étend. #23 d'abord = réinventer puis retravailler.
- Frontières : #21 seul à toucher domaine tickets (query sort) ; #22 présentation pure (docking ≠ LayoutTree) ; #23 seul à toucher app-tauri/composition root + nouveau port WindowGateway.

View File

@ -0,0 +1,15 @@
---
name: workstate-background-tasks-projection-fix
description: Décision d'archi pour rendre visibles Cancel/Retry dans le panneau Work — extension du read-model GetProjectWorkState, contrat DTO et borne de livraison.
metadata:
type: reference
---
**Défaut** : `AgentWorkState` (`crates/application/src/workstate/mod.rs:140`) ne portait pas les tâches de fond et `GetProjectWorkState` n'avait pas de dépendance `BackgroundTaskStore`. Le panneau Work masque la section (`ProjectWorkStatePanel.tsx:638`, `agent.backgroundTasks.length > 0`) → aucun bouton Cancel/Retry en live. Le frontend était déjà entièrement câblé (`normalizeAgent`/`normalizeBackgroundTask`), il ne manquait que la projection backend.
**Décision (frontière)** : étendre le read-model unique — ajouter `background_tasks: Vec<AgentBackgroundTaskState>` à `AgentWorkState` et brancher `BackgroundTaskStore` via un builder best-effort `with_background_tasks(store)` (calqué sur `with_conversation_sources`), `None` ⇒ zéro régression. **Pas** de fetch séparé côté front (garderait la jointure tâche→agent hors du domaine, casserait l'instantané cohérent, dupliquerait les cycles async). Aucun nouveau port/adapter. `list_background_tasks` (B7) reste comme read-model autonome, hors chemin du panneau.
**Contrat** : `AgentBackgroundTaskState` = miroir de `BackgroundTaskDto` (`dto.rs:2886`) sans owner/project. Projection dans `execute` = union `list_open_for_agent(agent.id)` (per-agent, running/queued/waiting) + `list_undelivered_completions()` (chargé UNE fois avant la boucle, dispatché par `owner_agent_id`, pour failed/cancelled → Retry). Best-effort strict : erreur store ⇒ `Vec::new()` pour l'agent, jamais d'`AppError` (live/busy/tickets intacts).
**Borne V1 assumée** : une tâche terminale **déjà livrée** (wake tiré, `completion_delivered=true`) n'est plus énumérable (droppée des deux listes) → disparaît du panneau ; Retry seulement dans la fenêtre non-livrée. Conforme à la note « retry session-scoped V1 » de `RetryBackgroundTask` (`background/mod.rs:212`). Historique Retry persistant = V2 (ajouterait `list_recent_terminal_for_agent` au port), ticketable si besoin.
**Wiring** : `.with_background_tasks(state.background_task_store.clone())` dans `crates/app-tauri/src/state.rs` (ancre `b7-wiring-anchor-state-rs`) ; le store y est déjà managé (utilisé par `list_background_tasks`). Lié à [[b8-in-app-trigger-run-in-background]], [[b8-command-runner-pty-framing]], [[background-tasks-first-class-design]], [[checkpoint-t1-wake-delivery-bug-fixed-rebuild-pending]].

View File

@ -149,6 +149,44 @@
],
"fallback": "allow"
}
},
{
"agentId": "c3e0d9d8-df36-4b00-b271-62d8aa78892c",
"permissions": {
"rules": [
{
"capability": "read",
"effect": "allow",
"paths": [
"**"
],
"commands": []
},
{
"capability": "write",
"effect": "deny",
"paths": [
"**"
],
"commands": []
},
{
"capability": "delete",
"effect": "deny",
"paths": [
"**"
],
"commands": []
},
{
"capability": "executeBash",
"effect": "allow",
"paths": [],
"commands": []
}
],
"fallback": "allow"
}
}
]
}

View File

@ -0,0 +1,11 @@
---
id: "028179b1-eaf4-41e9-9c1f-7c37125117e6"
order: 2
name: "Client - serveur"
status: "planned"
createdBy: {"kind":"user"}
updatedBy: {"kind":"user"}
createdAt: 1783329381447
updatedAt: 1783330421737
version: 4
---

View File

@ -0,0 +1,11 @@
---
id: "5afd6780-0f76-40d7-a10f-32ee52469d74"
order: 1
name: "UI rework"
status: "planned"
createdBy: {"kind":"user"}
updatedBy: {"kind":"user"}
createdAt: 1783329530306
updatedAt: 1783330421736
version: 3
---

View File

@ -0,0 +1,11 @@
---
id: "883534aa-7fc1-4d83-a9c0-17cac4a4eea5"
order: 4
name: "Modeles locaux"
status: "planned"
createdBy: {"kind":"user"}
updatedBy: {"kind":"user"}
createdAt: 1783757092388
updatedAt: 1783757205620
version: 2
---

View File

@ -0,0 +1,11 @@
---
id: "d8f3f37b-87ca-4509-9116-45a99bc711df"
order: 3
name: "Session limites handle"
status: "planned"
createdBy: {"kind":"user"}
updatedBy: {"kind":"user"}
createdAt: 1783329454681
updatedAt: 1783330421738
version: 3
---

41
.ideai/sprints/index.json Normal file
View File

@ -0,0 +1,41 @@
{
"version": 1,
"sprints": [
{
"id": "5afd6780-0f76-40d7-a10f-32ee52469d74",
"path": "5afd6780-0f76-40d7-a10f-32ee52469d74",
"order": 1,
"name": "UI rework",
"status": "planned",
"updatedAt": 1783330421736,
"version": 3
},
{
"id": "028179b1-eaf4-41e9-9c1f-7c37125117e6",
"path": "028179b1-eaf4-41e9-9c1f-7c37125117e6",
"order": 2,
"name": "Client - serveur",
"status": "planned",
"updatedAt": 1783330421737,
"version": 4
},
{
"id": "d8f3f37b-87ca-4509-9116-45a99bc711df",
"path": "d8f3f37b-87ca-4509-9116-45a99bc711df",
"order": 3,
"name": "Session limites handle",
"status": "planned",
"updatedAt": 1783330421738,
"version": 3
},
{
"id": "883534aa-7fc1-4d83-a9c0-17cac4a4eea5",
"path": "883534aa-7fc1-4d83-a9c0-17cac4a4eea5",
"order": 4,
"name": "Modeles locaux",
"status": "planned",
"updatedAt": 1783757205620,
"version": 2
}
]
}

View File

@ -0,0 +1,29 @@
---
issueRef: "#1"
version: 11
updatedBy: {"kind":"user"}
updatedAt: 1783175265022
---
- Cadrage: background-tasks-first-class-design. Arbitrage 5 écarts: b8-arbitration-outcomes.
- FAIT B8 runner PTY + boucle sink fermée + refactor point-2. QA vert. Commit backend 8cac147.
- FAIT UI cancel/retry câblés (cancel_background_task/retry_background_task). Commit frontend c179f93.
- FAIT déclencheur in-app: surface MCP idea_run_in_background (BE-1+BE-2), commit f08dae6. Ferme l'écart de périmètre « aucun producteur ».
- Dette tracée: #2 (PtyPort::wait + tee live), #3 (persistance sûre invocation/retry-after-reboot + énumération store), #5 (contrat DTO work-state).
## MAJ 2026-07-04 — part autonome de #1 clôturée (Main)
- AppImage rebuild frais confirmé (build 2026-07-03 23:56 > dernier source 23:53), inclut T1+T3, NO_STRIP=true.
- VALIDATION AUTOMATISÉE VERTE : cargo build --workspace OK ; cargo test -p application / -p app-tauri / -p infrastructure = 0 failed ; frontend npm run build OK + vitest 459/460. L'unique rouge = flake de timing src/features/permissions/permissions.test.tsx (passe en isolation 2/2, non touché par #1, pas une régression).
- COMMITS Git sur feature/background-tasks-first-class : eb9cc16 (code T1+T3, 21 fichiers) + ebd992e (19 notes mémoire + MEMORY.md). AUCUN merge vers develop. Runtime .ideai/{background-tasks,proposals,tickets}/ volontairement non commités.
## ⚠️ MAJ 2026-07-04 — IMPACT TOPOLOGIE (constat Git, cadrage #4)
- `feature/background-tasks-first-class` (#1) est DÉRIVÉE du `develop` ANORMAL/rembobiné (merge-base #1↔main = 1fc7869, pré-canonique ; a9653bc tip develop est ancêtre de #1). Donc #1 = baseline PRÉ-CANONIQUE (spawn_turn=0, run_turn batch, pas d'AgentTurnEvent).
- La vraie ligne d'intégration est `main` (canonique, release ~01/07), PAS le develop rembobiné (18 commits en retard sur main). ⇒ la cible de merge « → develop » prévue au point 3 ci-dessous est INVALIDÉE.
- #1 modifie massivement les fichiers divergés côté canonique : orchestrator/service.rs +425, domain/events.rs +184, lifecycle.rs +83, session/codex.rs. ⇒ portage sur canonique = CONFLITS NON TRIVIAUX attendus, pas un simple changement de cible.
- PLAN Git (Phase C, à faire quand on reprendra #1) : branche fraîche `feature/background-tasks-first-class-canonical` depuis main + cherry-pick séquentiel des 17 commits (ancienne branche gardée comme filet), conflits sémantiques service/events escaladés à DevBackend/Architect, puis QA avant tout merge.
- Décision de reconstruction de `develop` sur `main` : DESTRUCTIVE (rewrite branche partagée) → en attente validation utilisateur.
## RESTE — DÉPENDANT UTILISATEUR (Main ne peut pas seul)
1. Re-QA LIVE T3 : relancer l'AppImage fraîche puis vérifier onglet Work → Cancel/Retry (T3-a..f du cadrage workstate-background-tasks-projection-fix). QA non pilotable depuis sandbox → besoin app lancée.
2. T2 — reconcile après reboot (fin AVANT wake), protocole MANUEL, coordination reboot utilisateur.
3. [CIBLE À REVOIR] merge de #1 : ne plus viser le develop rembobiné. Porter #1 sur canonique (Phase C ci-dessus) PUIS merger vers develop-reconstruit/main. Dépend de la décision de reconstruction de develop.
Main travaille sur #4 (annonces inter-agent) en attendant la dispo utilisateur pour 1-2-3.

24
.ideai/tickets/1/issue.md Normal file
View File

@ -0,0 +1,24 @@
---
id: "ec19c041-7a44-456d-994d-f0dcb7c52f39"
number: 1
title: "Tâches de fond — finaliser B8 (runner PTY) et clôturer le chantier"
status: "closed"
priority: "medium"
links: []
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
createdBy: {"kind":"user"}
updatedBy: {"kind":"user"}
createdAt: 1783026129089
updatedAt: 1783175265022
version: 11
---
Description :
Le chantier « tâches de fond de 1re classe » est livré et QA-vert jusqu'à B7 inclus (mailbox bornée, rendez-vous inter-agent comme tâche durable, reconcile au boot, réveil du propriétaire, UI F1F4). Commité sur feature/background-tasks-first-class (e05edc6 backend, 5d88c95 frontend), non mergé. Reste à fermer les points suivants avant de considérer le chantier terminé.
Reste à faire :
- [ ] B8 — runner de commandes couplé PTY : créer un BackgroundTask{kind: Command} au spawn d'une commande longue (côté pty.rs / LocalProcessSpawner) et pousser exit/stdout/stderr dans le completion sink. C'est ce qui active le flux run_in_background complet : une commande qui finit après la fin du tour de l'agent réveille automatiquement son propriétaire avec le résultat. Aujourd'hui le sink est prêt mais aucun runner concret ne s'y abonne.
- [ ] UI cancel / retry des tâches de fond : les boutons existent mais sont désactivés faute de commande Tauri. Exposer cancel/retry (dépend de B8) et brancher l'UI.
- [ ] Validation live end-to-end de la feature dans l'app réelle : lancer un build/commande longue en run_in_background, laisser le tour se terminer, vérifier que le propriétaire est bien re-réveillé avec le résultat (et idem après redémarrage d'IdeA entre la fin et le wake — le reconcile doit retrouver la complétion).
- [ ] Merge feature/background-tasks-first-class → develop (sur validation) : résorbe aussi les 4 tests de protocole MCP périmés qui sont encore rouges sur develop (le fix d'alignement vit sur cette branche).

View File

@ -0,0 +1,6 @@
---
issueRef: "#10"
version: 5
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
updatedAt: 1783241883970
---

View File

@ -0,0 +1,15 @@
---
id: "37bfbb89-a8a5-475f-91bf-ecfd60ee4203"
number: 10
title: "Ticket par sprint"
status: "closed"
priority: "medium"
links: []
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
createdBy: {"kind":"user"}
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
createdAt: 1783183841014
updatedAt: 1783241883970
version: 5
---
J'aimerais pouvoir regrouper mes tickets en sprints. C'est a dire en catégories qui auraient elles meme un ordre d'execution (sprint 1, sprint 2 etc)

View File

@ -0,0 +1,6 @@
---
issueRef: "#11"
version: 6
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
updatedAt: 1783278726067
---

View File

@ -0,0 +1,15 @@
---
id: "dad53cb9-a818-4475-be12-fd3ba6c59638"
number: 11
title: "Interface de création de sprint"
status: "closed"
priority: "medium"
links: [{"target":"#10","kind":"dependsOn"}]
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
createdBy: {"kind":"user"}
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
createdAt: 1783183911244
updatedAt: 1783278726067
version: 6
---
Pour créer des sprints, j'aimerais une inteface de création dans laquelle je pourrais facilement ajouter/enlever des tickets de mes différents sprint

View File

@ -0,0 +1,6 @@
---
issueRef: "#12"
version: 5
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
updatedAt: 1783312900907
---

View File

@ -0,0 +1,15 @@
---
id: "fb8a73f9-c871-4513-aa09-b35fbbef637a"
number: 12
title: "Checkbox pour les filters des tickets"
status: "closed"
priority: "low"
links: []
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
createdBy: {"kind":"user"}
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
createdAt: 1783184054842
updatedAt: 1783312900907
version: 5
---
Pour les filtres des tickets, j'aimerais qu'on ai une checkbox plutot que la selection d'un seul filtre, de façon a pouvoir faire des combinaisons de plusieurs priorité et de plusieurs status par exemple

View File

@ -0,0 +1,6 @@
---
issueRef: "#13"
version: 4
updatedBy: {"kind":"user"}
updatedAt: 1783329397368
---

View File

@ -0,0 +1,16 @@
---
id: "036783fa-9856-4643-8c28-653b93ac36e1"
number: 13
title: "Server/client mode"
status: "open"
priority: "low"
sprint: "028179b1-eaf4-41e9-9c1f-7c37125117e6"
links: []
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
createdBy: {"kind":"user"}
updatedBy: {"kind":"user"}
createdAt: 1783184188272
updatedAt: 1783329397368
version: 4
---
J'aimerais pouvoir utiliser IdeA sous forme de client/server. C'est a dire que IdeA aurait son backend sur une machine et son interface qui serait un frontend web. Les agents etc seraient tous côté server. C'est a dire que la cli de Claude serait celle côté serveur, l'interface client ne serait que l'affichage frontend, une UI. Je pense que tout est faisable, il faudrait cependant parler du côté CLI. Est ce qu'il serait possible de garde rle CLI natif comme il est actuellement, de l'afficher côté client tout en gardant l'installation claude code, codex etc côté serveur ? Peut etre qu'en ssh ça passerait ? Ce ticket ne pourra pas être fait en autonomie par un agent sans une review agent-utilisateur avant.

View File

@ -0,0 +1,20 @@
---
issueRef: "#14"
version: 7
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
updatedAt: 1783456579694
---
## Production #14 — livrée dans develop (validation e2e live restante)
Périmètre figé avec l'utilisateur : adapter HTTP OpenAI-compatible **purement additif** (zéro régression Claude/Codex), parité **tool-calling + MCP** complète, canal HTTP natif robuste.
### Cycle
- Cadrage Architect : slug mémoire `ticket14-local-lan-openai-adapter-scoping` (asymétrie CLI-vs-serveur HTTP, port ToolInvoker in-process, reprise sans id provider).
- Backend (DevBackend) : variante `StructuredAdapter::OpenAiCompatible`, VO `HttpChatConfig`, `openai_compat.rs`, port `ToolInvoker` déléguant à la même OrchestratorService que le MCP. GO QA (domain 467 / application 530 / infrastructure 523 / app-tauri 247, 0 échec). Défaut réel corrigé : timeout post-contact mappé Io.
- Frontend (DevFrontend) : types DTO miroir, wizard/settings (endpoint/model/apiKeyEnv=nom de var/timeouts, validation miroir backend), profil de référence Ollama éditable, erreur endpoint rendue via bannière role="alert". GO QA (tsc 0 + vitest 59 fichiers / 566 tests, 0 échec). Défaut réel corrigé : erreur endpoint invisible dans le DOM.
### Git
- Backend `aab4bca`, frontend `d89380c`, merge `--no-ff` `3c6cd04` → develop. Branche feature supprimée. **Local uniquement, rien poussé.**
### Reste à faire (hors code)
- Validation e2e **live contre un vrai endpoint Ollama/LAN** : lancement cellule, conversation headless, reprise, délégation inter-agents, vivacité/timeout, endpoint indisponible/modèle absent (critère d'acceptation n°7). Non exercée en tests automatisés — c'est la raison du statut QA plutôt que closed.

View File

@ -0,0 +1,75 @@
---
id: "0c1f1991-b6db-47b7-bbef-2478bc7874ab"
number: 14
title: "Integration de profils IA locaux/LAN comme profils IdeA canoniques"
status: "qa"
priority: "medium"
sprint: null
links: []
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
createdBy: {"kind":"user"}
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
createdAt: 1783184495846
updatedAt: 1783456579694
version: 7
---
Objectif : permettre à IdeA d'utiliser des modèles hébergés localement ou sur le réseau local, par exemple Qwen via Ollama ou un runtime Docker exposant une API, avec le même niveau d'intégration produit que les profils Claude Code et OpenAI Codex CLI.
La finalité n'est pas un simple profil custom lancé dans un terminal brut. Ces profils doivent devenir de vrais profils IA IdeA : sélectionnables, lançables depuis les cellules, pilotés par la conversation headless canonique, compatibles avec les mécanismes de conversation/reprise, et utilisables par l'orchestration de la même manière que Claude/Codex.
## Cible produit
Ajouter une famille de profil structuré pour serveurs de modèles locaux ou LAN, idéalement basée sur une API HTTP compatible OpenAI/Ollama :
- endpoint configurable (`localhost`, machine LAN, container Docker, etc.) ;
- modèle configurable (`qwen...`, autre modèle local) ;
- authentification optionnelle via nom de variable d'environnement, jamais clé en clair ;
- profil visible et sélectionnable dans IdeA comme Claude/Codex ;
- cellule IdeA utilisable de façon canonique : conversation headless, historique, reprise, délégation, affichage des réponses et état de vivacité.
## Contraintes importantes
- Ne pas considérer le mode PTY/TUI comme suffisant pour ce ticket.
- Éviter une dépendance obligatoire à `ollama`, `docker` ou une commande shell quand une API HTTP suffit.
- Étudier explicitement les impacts commandes/headless :
- adapter HTTP natif ;
- wrapper CLI éventuel ;
- Docker/LAN ;
- sandbox et permissions ;
- absence ou présence de tool-calling/MCP côté modèle local.
- Le résultat doit s'intégrer dans le modèle existant `StructuredAdapter` / `AgentSessionFactory`, pas contourner le runtime IA d'IdeA.
## Travail attendu
1. Ajouter un adapter structuré pour modèle local/LAN, par exemple `StructuredAdapter::OpenAiCompatible` ou `LocalChat`.
2. Étendre le modèle de profil pour porter la configuration nécessaire : endpoint, model, apiKeyEnv optionnel, timeouts/liveness si nécessaire.
3. Implémenter une session headless `AgentSession` :
- `send(prompt)` ;
- émission de `ReplyEvent` ;
- gestion propre des erreurs réseau/modèle ;
- conversation id ou stratégie de reprise compatible avec le modèle IdeA.
4. Brancher l'adapter dans `StructuredSessionFactory`.
5. Rendre ces profils sélectionnables dans le wizard/settings au même titre que Claude/Codex.
6. Ajouter un profil de référence local, par exemple “Ollama / OpenAI-compatible local model”, éditable.
7. Vérifier l'intégration avec :
- lancement depuis une cellule ;
- conversation headless ;
- reprise ;
- changement de profil ;
- délégation inter-agents quand applicable ;
- état de vivacité/timeout ;
- erreurs endpoint indisponible/modèle absent.
## Critères d'acceptation
- Je peux configurer un profil Qwen hébergé via Ollama ou endpoint LAN.
- Je peux créer/lancer un agent IdeA avec ce profil comme avec Claude/Codex.
- La cellule utilise le chemin headless structuré, pas un terminal brut PTY.
- Les conversations apparaissent et se comportent comme les conversations Claude/Codex.
- La reprise ne mélange pas l'id logique IdeA avec un éventuel id provider.
- Un endpoint indisponible produit une erreur propre dans l'UI, sans bloquer IdeA.
- Les dépendances à `ollama`, Docker ou une CLI externe sont documentées et non imposées si le mode HTTP suffit.
- Tests backend couvrant factory, session adapter, erreurs HTTP et sérialisation du profil.
- Tests frontend couvrant configuration/sélection du nouveau profil.
Review agent-user requise avant démarrage d'implémentation pour figer le périmètre exact : HTTP natif uniquement, support d'un wrapper CLI, et niveau attendu de tool-calling/MCP pour les modèles locaux.

View File

@ -0,0 +1,6 @@
---
issueRef: "#15"
version: 2
updatedBy: {"kind":"user"}
updatedAt: 1783329462104
---

View File

@ -0,0 +1,22 @@
---
id: "5de121f3-1cd1-4a73-bcab-b0a43a7c257f"
number: 15
title: "Limites de session — re-livraison automatique parquée au reset (stretch B5, délégation durable)"
status: "open"
priority: "low"
sprint: "d8f3f37b-87ca-4509-9116-45a99bc711df"
links: [{"target":"#7","kind":"dependsOn"}]
agentRefs: []
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
updatedBy: {"kind":"user"}
createdAt: 1783208599385
updatedAt: 1783329462104
version: 2
---
Extrait du cadrage Architect du ticket #7 (baseline livrée : propagation inter-agent B1→B3 + F1/F2). Stretch non retenu dans #7.
Objectif : quand A délègue à B via idea_ask_agent et que B atteint sa limite, au lieu de demander à A de ré-interroger après le reset, PARQUER le waiter de A par (requester,target) ; au execute_resume de B (reprise auto au reset), RE-LIVRER la tâche d'origine et router la complétion vers le waiter de A s'il est encore vivant (sinon drop, contrainte in-memory).
Coût/risque (Architect) : rapproche du modèle de « délégation durable » tout en restant in-memory → fragile aux redémarrages/interruptions de A, ré-introduit une forme de blocage long côté A. À rattacher au design durable-delegation-runtime-agent-identity-design plutôt que de le traiter en rustine in-memory.
Dépend de : #7 (baseline inter-agent B1→B3). Voir mémoire ticket7-session-limit-interagent-cadrage.

View File

@ -0,0 +1,6 @@
---
issueRef: "#16"
version: 7
updatedBy: {"kind":"user"}
updatedAt: 1783405334263
---

View File

@ -0,0 +1,17 @@
---
id: "0a3585f0-9774-4109-a4e3-746dc5b2a4b2"
number: 16
title: "[UI] réorganisation des menus"
status: "closed"
priority: "medium"
sprint: null
links: []
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
createdBy: {"kind":"user"}
updatedBy: {"kind":"user"}
createdAt: 1783329576091
updatedAt: 1783405334263
version: 7
---
J'aimerais réorganiser les mesnus de façon à être un peu plus proche de l'interface d'un IDE plus classique. J'aiemrais plutot avoir des menu déroulant en haut de la fenetre que d'avoir cette barre latérale
Les menus déroulant pourraient afficher des fenetres flottantes, ce qui laisserait plus de place pour l'affichage.

View File

@ -0,0 +1,6 @@
---
issueRef: "#17"
version: 10
updatedBy: {"kind":"user"}
updatedAt: 1783405334977
---

View File

@ -0,0 +1,16 @@
---
id: "66aaef66-c70a-4153-81e9-a8f323686092"
number: 17
title: "[UI] fenetre de gestion de tickets"
status: "closed"
priority: "medium"
sprint: null
links: [{"target":"#16","kind":"dependsOn"},{"target":"#18","kind":"dependsOn"}]
agentRefs: []
createdBy: {"kind":"user"}
updatedBy: {"kind":"user"}
createdAt: 1783329807682
updatedAt: 1783405334977
version: 10
---
J'aimerais une fenetre dédiée à la gestion des tickets. Autant pour le listing que pour la création/edition des tickets. Dans un même temps, j'aimerais qu'on améliore la gestion des tickets lié lorsque l'on édite les tickets en utilisant la popup du ticket #18. J'aiemrais aussi que l'agent permettant l'écriture des tickets soit proposé de façon plus évidante. Je pense qu'un bouton en bas à droite de la fenetre d'édition ouverte serait bien.

View File

@ -0,0 +1,6 @@
---
issueRef: "#18"
version: 9
updatedBy: {"kind":"user"}
updatedAt: 1783405335941
---

View File

@ -0,0 +1,16 @@
---
id: "5c17d6d3-c537-45b1-a17e-9c4edee963d1"
number: 18
title: "[UI] Créer une popup de selection de ticket"
status: "closed"
priority: "medium"
sprint: null
links: []
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
createdBy: {"kind":"user"}
updatedBy: {"kind":"user"}
createdAt: 1783330038271
updatedAt: 1783405335941
version: 9
---
Pour différentes feature comme la création de sprint ou même le link d'un ticket à d'autres, il est necessaire de selectionner un ticket. Pour ça j'aimerais un popup qui puisse être utilisée dans ces différentes features pour la selection d'un ticket. Il faudrait une barre de recherche ainsi que la possibilité d'appliquer des filtres sur l'avancée ainsi que la priorité des tickets, comme pour le listing principal des tickets.

View File

@ -0,0 +1,6 @@
---
issueRef: "#19"
version: 8
updatedBy: {"kind":"user"}
updatedAt: 1783405337651
---

View File

@ -0,0 +1,16 @@
---
id: "b1c84dd2-cef7-4e48-a501-1a30eedb02f2"
number: 19
title: "[UI] implémentation de la popup de selection de ticket dans la création d'un sprint"
status: "closed"
priority: "low"
sprint: null
links: [{"target":"#18","kind":"dependsOn"}]
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
createdBy: {"kind":"user"}
updatedBy: {"kind":"user"}
createdAt: 1783330352910
updatedAt: 1783405337651
version: 8
---
Lors de l'edition d'un sprint, j'aimerais que pour selectionner un ticket, ça soit la popup de selection de ticket qui soit utilisée

View File

@ -0,0 +1,6 @@
---
issueRef: "#2"
version: 2
updatedBy: {"kind":"user"}
updatedAt: 1783184142093
---

24
.ideai/tickets/2/issue.md Normal file
View File

@ -0,0 +1,24 @@
---
id: "0a492d45-e195-4df7-a2ad-65649372abf0"
number: 2
title: "Tâches de fond — dette A : PtyPort::wait/try_wait + tee live UI + robustesse détection de fin"
status: "open"
priority: "low"
links: []
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
updatedBy: {"kind":"user"}
createdAt: 1783082860742
updatedAt: 1783184142093
version: 2
---
Dette issue de l'arbitrage B8 (mémoire projet: b8-arbitration-outcomes, point 1).
B8 a été accepté sans rendu live des tâches de fond. Le cœur complétion→wake fonctionne, mais la détection de fin du runner repose sur l'EOF du stream PTY single-consumer, ce qui est fragile et empêche un tee UI (une re-souscription UI casserait la détection EOF).
Reste à faire :
- Ajouter `PtyPort::wait` / `try_wait` pour découpler la détection d'exit de la consommation d'output (retirer l'EOF-comme-proxy-de-fin).
- Une fois découplé, brancher un tee de la sortie PTY vers l'UI pour le rendu live des tâches de fond (F-live).
- NE PAS introduire de broadcast multi-consommateur (décision Architect).
Périmètre touchant un port figé (PtyPort) : cadrage Architect requis avant implémentation.

View File

@ -0,0 +1,6 @@
---
issueRef: "#20"
version: 2
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
updatedAt: 1783331176736
---

View File

@ -0,0 +1,26 @@
---
id: "1be19ee5-fd03-42ea-8df6-da18704807a9"
number: 20
title: "[Backend] Dette ticket_list — matching #ref dans text + curseur opaque/stable"
status: "open"
priority: "low"
sprint: null
links: [{"target":"#18","kind":"relatesTo"}]
agentRefs: []
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
createdAt: 1783331172226
updatedAt: 1783331176736
version: 2
---
Dette backend identifiée pendant le cadrage du sprint UI rework (gate G4), non bloquante pour la popup #18 qui démarre sur le contrat actuel.
Deux écarts sur la commande `ticket_list` (handler `crates/app-tauri/src/tickets.rs:681`, filtre store `crates/infrastructure/src/issues.rs`) :
1. Recherche `text` : appliquée serveur sur titre (issues.rs:322) + description/carnet (issues.rs:326/465) mais PAS sur le `#ref`/numéro de ticket. Étendre le matching `text` à `issue_ref` pour permettre la recherche par numéro dans la popup de sélection.
2. `cursor` : actuellement un offset numérique parsé en usize avec fallback silencieux à 0 si invalide (tickets.rs:1231), non opaque et non stable face à des mutations entre pages. Contractualiser un curseur opaque + stable (ordre déterministe préservé).
Garde G3-bis déjà en place côté frontend : `TicketPicker`/`useTicketSearch` traitent `cursor` comme un token opaque (jamais construit/incrémenté côté client), donc la migration vers un curseur opaque backend n'exigera AUCUN changement frontend — cette dette est non-breaking.
statuses[]/priorities[] sont conformes (OR intra-facette, AND inter-facette, testés) — rien à faire de ce côté.

View File

@ -0,0 +1,6 @@
---
issueRef: "#21"
version: 7
updatedBy: {"kind":"user"}
updatedAt: 1783437364397
---

View File

@ -0,0 +1,16 @@
---
id: "4ffe9f90-2264-4861-b7da-f5535b0a622f"
number: 21
title: "[UI] pouvoir trier les ticket par..."
status: "closed"
priority: "low"
sprint: null
links: [{"target":"#16","kind":"dependsOn"},{"target":"#18","kind":"dependsOn"}]
agentRefs: []
createdBy: {"kind":"user"}
updatedBy: {"kind":"user"}
createdAt: 1783331825658
updatedAt: 1783437364397
version: 7
---
J'aiemrais qu'on ajoute une option "trier par..." dans la popup de selection des tickets et dans la liste principale des tickets. On pourrait trier par numéro de ticket, priorité, status, ou titre du ticket. Avec chaque fois croissant/decroissant.

View File

@ -0,0 +1,6 @@
---
issueRef: "#22"
version: 5
updatedBy: {"kind":"user"}
updatedAt: 1783437364871
---

View File

@ -0,0 +1,16 @@
---
id: "2b90fd78-8fce-449d-ab61-e5aee5ede08c"
number: 22
title: "[UI] Anchor de views"
status: "closed"
priority: "medium"
sprint: null
links: []
agentRefs: []
createdBy: {"kind":"user"}
updatedBy: {"kind":"user"}
createdAt: 1783405028818
updatedAt: 1783437364871
version: 5
---
J'aimerais qu'à la manière d'un IDE classique, il soit possible d'ancrer des views sur le côté droite ou gauche et de proposer un format vertical. 9a peut être utilise par exemple pour git, les work, les agents, les skills etc. J'ai en tête les IDE comme Visual COde ou la suite Jetbrains par exemple.

View File

@ -0,0 +1,6 @@
---
issueRef: "#23"
version: 6
updatedBy: {"kind":"user"}
updatedAt: 1783437365276
---

View File

@ -0,0 +1,16 @@
---
id: "ed815933-ed48-466d-b265-3724f3ea943b"
number: 23
title: "[UI] rendre les fenêtres View séparée de IdeA"
status: "closed"
priority: "medium"
sprint: null
links: []
agentRefs: []
createdBy: {"kind":"user"}
updatedBy: {"kind":"user"}
createdAt: 1783405188644
updatedAt: 1783437365276
version: 6
---
J'aimerais que les fenêtre qu'on affiche via le menu View soient des fenêtre à part entières, qu'on puisse les déplacer sur les différents écrans, les mettre fullscreen etc, que ça soit des enetres "systeme" normales

View File

@ -0,0 +1,25 @@
---
issueRef: "#25"
version: 11
updatedBy: {"kind":"user"}
updatedAt: 1783491435654
---
## Root cause
`OpenTicketAssistant::execute` fabriquait un plan OS-sandbox bespoke via `SandboxPlan::project_read_only` (grant RO sur project_root, posture Deny). Sous Landlock, un grant RO fait *handle* la classe read/exec (`AccessFs::from_read(V1)` inclut Execute). Le binaire CLI (codex/claude) vit hors du project root ⇒ `execve` refusé ⇒ EACCES (os error 13) au spawn sandboxé. Indépendant de la CLI. Les agents workspace n'ont pas le bug (plan no-op sous posture Allow).
## Correctif (backend-pur, zéro port/DTO)
Décision Main : option fallback `None`. La surface assistant de ticket passe désormais `None` en 5e arg de `factory.start` (aligné sur les agents workspace). Garde-fous restants : policy MCP `idea_ticket_read/update*` + advisory LP3. Helper `ticket_assistant_sandbox` et preset `SandboxPlan::project_read_only` supprimés (plus aucun appelant).
Fichiers : crates/application/src/ticket_assistant.rs (+tests), crates/domain/src/sandbox.rs.
## Validation
- `cargo test --workspace` VERT (app-tauri 63, application 80, infrastructure 254, domain OK).
- Test ciblé : `factory.start` reçoit `SessionPlan::None` + `sandbox.is_none()`.
- Tests Landlock infra verts sur la machine.
- RÉSERVE : repro live Codex+Claude non exécutée — nécessite rebuild + swap de l'AppImage active + relance IdeA (interrompt la session).
## Git
Commit `961a2ca` → merge `--no-ff` `cb20fab` dans develop. Local only, pas de push.
## Reste à faire
Repro live après rebuild AppImage pour clôture définitive. Dette différée : write-fence OS uniforme sur toutes les surfaces (cf. cadrage Architect) si l'on veut une clôture write-OS.

View File

@ -0,0 +1,16 @@
---
id: "6123b180-b36b-4807-ba31-46d60d3ef022"
number: 25
title: "[Bug] Codex/Claude erreur lors de l'utilisation dans l'edition d'un ticket"
status: "closed"
priority: "medium"
sprint: null
links: []
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
createdBy: {"kind":"user"}
updatedBy: {"kind":"user"}
createdAt: 1783406391625
updatedAt: 1783491435654
version: 11
---
J'ai voulu créer un ticket via Codex dasn l'interface d'edition d'un ticket, mais j'ai eu cette erreur: process error: agent session start failed: codex: Permission non accordée (os error 13). J'aéi séléctionné que je voulais utiliser codex et l'erreur est apparue quand j'ai essayé de lui donné mon premier message. Je me rend compte que j'ai la même erreur avec Claude

View File

@ -0,0 +1,6 @@
---
issueRef: "#26"
version: 4
updatedBy: {"kind":"user"}
updatedAt: 1783491079604
---

View File

@ -0,0 +1,16 @@
---
id: "2201b898-9c45-4057-9014-5284897507ad"
number: 26
title: "[UI] refonte totale de la gestion des menus via agent UX"
status: "open"
priority: "medium"
sprint: "5afd6780-0f76-40d7-a10f-32ee52469d74"
links: []
agentRefs: []
createdBy: {"kind":"user"}
updatedBy: {"kind":"user"}
createdAt: 1783437525356
updatedAt: 1783491079604
version: 4
---
J'aimerais une refonte totale de l'UX et de l'UI d'IdeA pour tout ce qui touche aux menus. Je n'aime pas la disposition actuelle. Il est important de garder l'aspect fenêtre flottantes, le fait de pouvoir anchor les fenêtres sur le côté d'IdeA, mais je n'aime pas le fait qu'il y ai deux listes déroulantes View et Window. Onne comprends pas vraiemnt la différence. De plus, l'affichage de l'onglet du projet avec le nom "Projet : IdeA" sur lequel cliquer pour changer de projet je n'aime pas. Je pense peut être plutot ajouter un + à côté de l'onglet du projet pour pouvoir ouvrir plusieurs projet en parallèle ? (A l''approbation de UX). Par contre je ne veux pas qu'on touche aux cellules des agents, elles sont très bien comme ça, on se contente des menus et des fenêtres.

View File

@ -0,0 +1,6 @@
---
issueRef: "#27"
version: 3
updatedBy: {"kind":"user"}
updatedAt: 1783440116446
---

View File

@ -0,0 +1,16 @@
---
id: "d7459ae0-31ae-49b2-bd93-2419397b0343"
number: 27
title: "[Bug] L'outil assistance IA n'a pas les commandes MCP"
status: "open"
priority: "high"
sprint: null
links: []
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
createdBy: {"kind":"user"}
updatedBy: {"kind":"user"}
createdAt: 1783439818722
updatedAt: 1783440116446
version: 3
---
Il semblerait que l'outil assistance IA des tickets n'ait pas connaissance des tools MCP pour l'edition des tickets. J'ai essayé de demandé à l'assistant de modifié le ticket pour que ça aille dans le sens de la conversation que j'ai eu avec lui et il m'a dabord donné un .md. Quand je lui ai demandé de modifier lui même le ticket, il est allé dans le fichier directement pour apporter des modifications au lieu d'utiliser le tool MCP d'IdeA

View File

@ -0,0 +1,6 @@
---
issueRef: "#28"
version: 3
updatedBy: {"kind":"user"}
updatedAt: 1783491458775
---

View File

@ -0,0 +1,33 @@
---
id: "9d8ec8cf-312a-4f90-8199-e37b183ba11e"
number: 28
title: "First-run wizard : bouton « Save and continue » figé (busy bloqué par l'auto-détection)"
status: "closed"
priority: "high"
sprint: null
links: []
agentRefs: []
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
updatedBy: {"kind":"user"}
createdAt: 1783458330440
updatedAt: 1783491458775
version: 3
---
## Symptôme (confirmé live, build 0.3.0 @2026-07-07 22:46, = develop 3c6cd04)
Au premier lancement, le wizard affiche les profils de référence (Claude/Codex/Ollama), l'utilisateur remplit le profil Ollama et coche la case, mais **« Save and continue » ET « Detect installed CLIs » restent grisés** → impossible de finir le first-run. Confirmé par l'utilisateur : les deux boutons sont grisés.
## Diagnostic (source + JS embarqué vérifiés)
- Le JS embarqué contient exactement `disabled: n.busy` sur le bouton (`FirstRunWizard.tsx:103`) — **aucune** condition de validation. Donc bouton grisé ⇔ `busy` reste `true`.
- `useFirstRun.reload()` : `setBusy(true)` → affiche les lignes (d'où formulaire visible/éditable) → `await detectProfiles(refs)` (auto-détection à l'ouverture) → `finally { setBusy(false) }`. Le `finally` ne s'exécute que **si la détection se termine**. Si la promesse `detectProfiles` ne se résout jamais, `busy` reste figé → les deux boutons restent grisés.
## Causes racines suspectées (2 défauts #14 qui se combinent)
1. **Profil Ollama sans commande de détection** : `detect: None``detection_spec` retombe sur `"{command} --version"` = `openai-compatible --version` (`runtime/mod.rs:64`), binaire inexistant → ligne « ✗ not found », et surtout probe inutile.
2. **`block_on` sur runtime tokio imbriqué** dans le chemin de détection : `DetectProfiles::execute` (`usecases.rs:79`) appelle `runtime.detect()` synchrone → `futures_block_on` construit un runtime current-thread et `block_on` (`runtime/mod.rs:231`) **à l'intérieur** de la commande async Tauri `detect_profiles` (`commands.rs:752`). Pattern qui peut paniquer (« Cannot start a runtime from within a runtime ») / rester pendu → la promesse ne se résout jamais. Se déclenche probablement sur une machine **sans Claude/Codex installés**.
## Attendu du fix
- `busy` doit **toujours** revenir à `false` même si la détection panique/pend (le `finally` ne doit pas dépendre du résultat de la détection ; détection best-effort, non bloquante, éventuellement bornée par timeout).
- Donner une commande/stratégie de détection correcte au profil OpenAI-compatible (ou ne pas le sonder comme un CLI — c'est un endpoint HTTP, pas un binaire).
- Corriger le `block_on` imbriqué dans le runtime de détection (ne pas bloquer dans un contexte async Tauri).
## Validation QA
Reproduire sur une machine **sans** Claude/Codex installés + Ollama lancé ; vérifier que le wizard reste utilisable (boutons cliquables) et que le profil Ollama se persiste et démarre.

View File

@ -0,0 +1,6 @@
---
issueRef: "#29"
version: 4
updatedBy: {"kind":"user"}
updatedAt: 1783491123831
---

View File

@ -0,0 +1,16 @@
---
id: "51f19ca1-6b67-491b-b750-735c48a89d4b"
number: 29
title: "[UI] Garder les filtres des tickets entre deux redemarrage"
status: "open"
priority: "low"
sprint: "5afd6780-0f76-40d7-a10f-32ee52469d74"
links: []
agentRefs: []
createdBy: {"kind":"user"}
updatedBy: {"kind":"user"}
createdAt: 1783491008871
updatedAt: 1783491123831
version: 4
---
J'aimerais garder les filtres des tickets en mémoire entre deux redemarrage ou ouverture de la fenetre ou d ela popup de choix de tickets.

View File

@ -0,0 +1,6 @@
---
issueRef: "#3"
version: 2
updatedBy: {"kind":"user"}
updatedAt: 1783184146749
---

22
.ideai/tickets/3/issue.md Normal file
View File

@ -0,0 +1,22 @@
---
id: "fedd343e-f9c1-467b-bab4-455f51907de7"
number: 3
title: "Tâches de fond — dette B : persistance sûre de l'invocation (retry-after-reboot, secrets) + énumération terminale/projet du store"
status: "open"
priority: "low"
links: []
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
updatedBy: {"kind":"user"}
createdAt: 1783082860758
updatedAt: 1783184146749
version: 2
---
Dette issue de l'arbitrage B8 (mémoire projet: b8-arbitration-outcomes, points 2 et 5).
En V1, le registre d'invocations pour le retry est in-memory et session-scoped (le SpawnSpec porte des secrets, interdit dans .ideai/background-tasks/*.json qui voyage avec le projet). Conséquences à résorber :
- Retry-after-reboot : persistance SÛRE de l'invocation (redaction des secrets et/ou store machine-local hors projet) pour permettre le retry après un redémarrage d'IdeA.
- Énumération store : aujourd'hui `list_background_tasks` sans agentId ne renvoie que les complétions non livrées du projet ; le store n'énumère pas les tâches terminales/ouvertes par projet (historique completed/failed partiel). Enrichir BackgroundTaskStore pour l'énumération terminale/projet.
Cadrage Architect requis (persistance de secrets = sensible).

View File

@ -0,0 +1,6 @@
---
issueRef: "#30"
version: 3
updatedBy: {"kind":"user"}
updatedAt: 1783491223434
---

View File

@ -0,0 +1,16 @@
---
id: "f2c4ed9c-ba54-42c1-90aa-0345d4ff4c6b"
number: 30
title: "[Bug] Le handle de limite session n'est pas fonctionnel quand c'est Main qui limite session"
status: "open"
priority: "medium"
sprint: null
links: [{"target":"#27","kind":"blockedBy"}]
agentRefs: []
createdBy: {"kind":"user"}
updatedBy: {"kind":"user"}
createdAt: 1783491181648
updatedAt: 1783491223434
version: 3
---
Lorsque l'agent qui atteint sa limite est celui à qui ont parle, la limite de session n'est pas récupérée par IdeA et n'est pas gérée

View File

@ -0,0 +1,6 @@
---
issueRef: "#31"
version: 1
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
updatedAt: 1783491197541
---

View File

@ -0,0 +1,31 @@
---
id: "83d320f1-766b-4744-b518-8e39a2518e9c"
number: 31
title: "Détection de profils séquentielle : N × 800 ms au pire, résultat potentiellement partiel"
status: "open"
priority: "low"
sprint: null
links: [{"target":"#28","kind":"relatesTo"}]
agentRefs: []
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
createdAt: 1783491197541
updatedAt: 1783491197541
version: 1
---
## Origine
Relevé par Git en revue du diff du ticket #28 (merge 710fa8f), hors périmètre du fix.
## Constat
Depuis #28, `DetectProfiles::execute` séquence les `await` candidat par candidat, chaque sonde bornée par `tokio::time::timeout(800ms)`. N profils indisponibles coûtent donc **N × 800 ms** au pire.
Côté frontend, `DETECT_TIMEOUT_MS = 3 s` relâche le flag `detecting`. À partir de ~4 candidats muets, le tour de détection dépasse ce délai : l'indicateur « Detecting… » disparaît avant que les résultats n'arrivent.
## Impact
Cosmétique. L'UI **ne gèle plus** (c'est bien l'objet de #28, corrigé) ; le risque résiduel est un affichage de disponibilité qui arrive après la retombée de l'indicateur.
## Piste de fix
Paralléliser les sondes avec `futures::future::join_all` dans `DetectProfiles::execute` (`crates/application/src/agent/usecases.rs`) : le coût au pire retombe à ~800 ms quel que soit N, largement sous le timeout UI.
## Validation
Test `#[tokio::test(flavor = "multi_thread")]` sur `DetectProfiles::execute` avec N candidats muets, asserting que la durée totale reste bornée par ~1 × timeout et non N ×.

View File

@ -0,0 +1,6 @@
---
issueRef: "#32"
version: 6
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
updatedAt: 1783723378815
---

View File

@ -0,0 +1,23 @@
---
id: "9291e7ad-a300-4f15-83b1-93f56f6e1b79"
number: 32
title: "[Bug] Erreur CLI Ollama profile"
status: "closed"
priority: "medium"
sprint: null
links: [{"target":"#14","kind":"relatesTo"}]
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
createdBy: {"kind":"user"}
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
createdAt: 1783491571863
updatedAt: 1783723378815
version: 6
---
J'ai créé un profile IA Ollama et j'ai ajouté un agent avec ce profile (agent TestOllama), mais quand
j'essaie d'afficher sa cli j'ai un bandeau rouge qui dit "Échec du lancement de l'agent : process error: pty spawn
failed: Unable to spawn openai-compatible because:
No viable candidates found in PATH "/tmp/.mount_IdeA_0mlgJnH/usr/bin/:/tmp/.mount_IdeA_0mlgJnH/usr/sbin/:/tmp/.mount_
IdeA_0mlgJnH/usr/games/:/tmp/.mount_IdeA_0mlgJnH/bin/:/tmp/.mount_IdeA_0mlgJnH/sbin/:/home/anthony/.local/bin:/run/us
er/1000/fnm_multishells/4610_1783490075749/bin:/home/anthony/.local/share/fnm:/home/anthony/go/bin:/home/anthony/.car
go/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:
/usr/bin/core_perl:/home/anthony/.local/share/JetBrains/Toolbox/scripts"

View File

@ -0,0 +1,6 @@
---
issueRef: "#33"
version: 1
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
updatedAt: 1783492190391
---

Some files were not shown because too many files have changed in this diff Show More