Files
IdeA/.ideai/memory/ticket101-cross-talk-multi-project-rootcause.md

6.2 KiB

name, description, metadata
name description metadata
ticket101-cross-talk-multi-project-rootcause memory note ticket101-cross-talk-multi-project-rootcause
type
project
slug: ticket101-cross-talk-multi-project-rootcause title: "Ticket #101 — défaut d'isolation multi-projet (registres runtime globaux par AgentId)" type: reference description: Cause racine du cross-talk entre projets ouverts simultanément (branche hybride wear-os + notification de tâche backend perdue) : les stores disque IdeA sont scoppés par projet, mais plusieurs registres mémoire runtime critiques restent indexés par AgentId seul, sans re-mint des UUID à l'ouverture. Plan de correction figé en 6 lots (clé RuntimeAgentKey).

Ticket #101 — défaut d'isolation multi-projet (cause racine)

Symptômes (2 manifestations, même défaut)

  • A — contamination du contexte d'inférence : un agent a créé une pseudo-branche feature/ticket91-wear-os-watch-sync (ticket #91 purement front) où le suffixe wear-os-watch-sync appartient à l'AUTRE projet de l'utilisateur. Le nom n'apparaît dans aucun fichier .ideai/ du projet IdeA → contamination au niveau du contexte d'inférence runtime (conversation injectée à l'agent Git mélangeait les projets). Preuve topo préservée : main divergé de develop (da907b8 vs 6a87c46), feature/ticket99-agent-model-configuration créée depuis main au lieu de develop.
  • B — notification de tâche backend perdue / agent qui s'arrête (sujet original #101) : un agent OpenCode lance idea_run_in_background puis s'arrête, sans retour de notification garanti.

Cause racine (Architect, 2026-07-25)

Stores disque correctement scoppés par projet (mémoire, contexte, handoff, live-state lus depuis input.project.root ; agents persistés dans .ideai/agents.json par root projet). MAIS les agents ne re-mint pas leurs UUID à l'ouverture → deux projets (surtout si l'un est une copie) peuvent porter les mêmes AgentId.

Plusieurs registres mémoire runtime restent globaux, indexés par AgentId seul :

  • ConversationRegistry global + ConversationId dérivé des UUID agents seuls — crates/domain/src/conversation.rs:71, crates/infrastructure/src/conversation/mod.rs:41, orchestrateur crates/application/src/orchestrator/service.rs:2192.
  • Sessions PTY/structured retrouvées par agent_id seul — crates/application/src/terminal/registry.rs:182,437 ; réutilisation session service.rs:2293,2368.
  • Verrous ask, busy-state, liveness, délégations différées par AgentId seul — service.rs:418, crates/infrastructure/src/input/mod.rs:47.
  • Inbox/mailbox par AgentId seul — crates/domain/src/inbox.rs:68,156, input/mod.rs:1052, crates/infrastructure/src/mailbox/mod.rs:44.
  • Wake par AgentId seul — crates/application/src/orchestrator/wake.rs:87, provider crates/backend/src/lib.rs:687.

→ Un agent du projet courant peut se rattacher à la conversation/session/inbox d'un autre projet (collision d'UUID) : contamination d'inférence (A) ET complétion livrable/bloquée/réveillant la mauvaise session (B).

Pour B : runtime vs modèle

La chaîne sink→project_id→wake est correcte jusqu'au bridge (crates/infrastructure/src/background_task/sink.rs:23,142 ; crates/backend/src/lib.rs:2228 recharge le projet par project_id puis wake_agent). Le défaut réapparaît juste après (inbox/mailbox/busy/session par AgentId seul). Runtime IdeA suspecté en premier. Le modèle GLM 5.2 n'est l'hypothèse principale que si les logs prouvent (pour le bon project_id) : enqueue + wake + BackgroundTaskCompletionDelivered émis sans reprise utile. Traces : lib.rs:2238, wake.rs:93.

Plan de correction (6 lots, figé)

  1. Introduire RuntimeAgentKey { project_id, agent_id } ; interdire toute map app-wide indexée par AgentId seul.
  2. Propager la clé dans AgentInbox, InputMediator, AgentMailbox, busy/liveness/deferred, wake, session lookup, ask-locks. Correctif structurel commun à A et B.
  3. Qualifier les conversations par projet : ConversationId + ConversationRegistry intègrent ProjectId.
  4. Requalifier TerminalSessions/StructuredSessions par (project_id, agent_id) ; AppWakeSessionProvider refuse toute session vivante d'un autre projet.
  5. Audit des autres états mémoire similaires (session_limit, tables de reprise/verrous par agent) pour éviter une demi-correction.
  6. Télémétrie project_id explicite sur les logs diag critiques de wake/routage.

Invariants

  • Sources de contexte sur disque restent root-scopées (ne pas régresser).
  • Templates/profils globaux restent globaux produit — ne doivent pas devenir vecteurs de session/conversation partagée.
  • Un projet non-actif doit continuer à recevoir ses wakes et complétions.
  • Pas de régression OpenCode/Codex/Claude (correctif transverse runtime, pas moteur-spécifique).
  • Aucun nouveau DTO frontend pour la correction minimale.

QA — critère de vérité

Test d'intégration non-cross-talk : 2 projets ouverts simultanément contenant volontairement les mêmes AgentId :

  • délégation dans projet A ne réutilise jamais une session/conversation vivante de projet B ;
  • complétion (project A, agent X) n'entre ni dans l'inbox ni dans la session de (project B, agent X) ;
  • le wake d'un projet non-actif fonctionne quand même ;
  • une collision d'ids qui échouait avant devient verte sur PTY, structured et background wake.

Hors périmètre

  • #91 (popup de notification) : purement front, indépendant du défaut runtime.
  • Remint systématique des AgentId à l'ouverture : piste complémentaire non retenue dans le fix minimal (la clé runtime scellée par projet rend la collision inoffensive même sans remint).

Topologie

  • Branche de travail à créer pour le fix (Git décidera). Base develop (6a87c46). main/feature/ticket99-agent-model-configuration divergent sur da907b8 (preuves préservées, à nettoyer après enquête).

Lié

  • #91 (relatesTo) : popup de notification — front pur.
  • Mémoire background-tasks-first-class-design + b8-command-runner-pty-framing : design du flux de complétion (le défaut est en aval du sink).
  • Mémoire mcp-bridge-and-delegation-runtime-notes : règle rebuild AppImage (le binaire qui tourne = AppImage, pas les sources).