docs(live-state): clôture programme persistance/live-state (LS8)

Documentation de clôture du programme live-state/persistance (LS1→LS7, livré @ fd7adbb) :
- docs/LS8-live-state-persistence-closure.md : référence durable (4 stores, flux
  d'injection, chemin chaud/froid, règle de frontière, points ouverts).
- ARCHITECTURE.md : §19 marqué LIVRÉ, §14.1 items 7-8 (live-state + handoff),
  §18.5 catalogue MCP de 14 outils, nouvelle §21 (cartographie de clôture).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-22 17:53:02 +02:00
parent fd7adbb8a0
commit 36be0cb396
2 changed files with 158 additions and 5 deletions

View File

@ -647,11 +647,13 @@ IdeA/
**Convention file généré par IdeA** : IdeA écrit dans ce dossier le fichier conventionnel attendu par le profil (`CLAUDE.md`, `AGENTS.md`, etc.). Ce fichier contient :
1. Le **chemin absolu du project root** (pour que l'agent sache où opérer).
2. Le contrat d'**orchestration IdeA** (délégation via `.ideai/requests`, pas via les subagents natifs du fournisseur).
2. Le contrat d'**orchestration IdeA** + brief capacités (délégation via outils `idea_*` en surface MCP, sinon via `.ideai/requests` — jamais les subagents natifs du fournisseur).
3. Le **contexte projet partagé** (`.ideai/CONTEXT.md`), si présent.
4. La **persona/rôle** de l'agent (son `.md` dans `.ideai/agents/`).
5. Les **skills actifs** assignés à cet agent (voir §14.2).
6. Le **rappel mémoire** du projet (index/hooks), si présent (voir §14.5.4).
6. Le **rappel mémoire** du projet (index/hooks — pointeurs), si présent (voir §14.5.4).
7. L'**état du projet** (section `# État du projet`) — projection *live-state* maigre « qui fait quoi maintenant », bornée (cap `LIVE_STATE_INJECT_MAX`, agent lancé exclu, ordre manifeste, vide ⇒ omise) (LS4 ; voir §21 et `docs/LS8`).
8. La **reprise de la conversation** (section `# Reprise de la conversation`) — *handoff* distillé du fil, **borné** (`HANDOFF_SUMMARY_MAX_CHARS=4096`) à l'écriture **et** à l'injection (LS5 ; voir §21). Omise sans handoff.
**Avantages** :
- Zéro collision entre agents, même N instances du même profil.
@ -1926,16 +1928,16 @@ Nouvelles commandes (jumelles des commandes PTY existantes ; réutilisent `resol
- **Vivant de bout en bout** : `apply_mcp_config` matérialise `.mcp.json` dans le **run dir isolé** de l'agent **AVANT** le split structuré/PTY (`crates/application/src/agent/lifecycle.rs` ~1094) ⇒ Claude/Codex le lisent **nativement**. La déclaration porte l'**exe réel** injecté par `McpRuntime` (`$APPIMAGE` sinon `current_exe`, `crates/app-tauri/src/mcp_endpoint.rs:139` `idea_exe_path`), l'**endpoint loopback** du projet, le `--project` et le `--requester` (agent réel, fin du `"mcp"` figé).
- **Endpoint loopback** = **UDS** (Linux/macOS) / **named pipe** (Windows), **zéro port réseau** ; source de vérité unique `mcp_endpoint(project_id)` (`mcp_endpoint.rs`), bindé à l'open / fermé au close (`crates/app-tauri/src/state.rs` `ensure_mcp_server`/`bind_endpoint`). **Fix D1** : cadavre `.sock` (run SIGKILL) **unlinké avant bind** (`state.rs` ~1019-1033) — sinon `EADDRINUSE`.
- **Serveur** `McpServer` (`crates/infrastructure/src/orchestrator/mcp/server.rs`) = **jumeau du `FsOrchestratorWatcher`** : autre porte sur le **même** `OrchestratorService::dispatch`, `serve(conn)` **par pair**. Transport `StdioTransport` (JSON Lines stdin/stdout du pont) + `MemoryTransport` (tests, sans socket ni process). Pont = sous-commande `mcp-server` du binaire app-tauri (`mcp_bridge.rs`).
- **Outils** `idea_ask_agent` / `idea_reply` / `idea_list_agents` (`mcp/tools.rs`) mappés 1:1 vers `OrchestratorCommand` ; `dispatch` appelé **à l'identique** par les trois portes (fichier, MCP, UI).
- **Outils** (`mcp/tools.rs`) — **catalogue de 14**, tous mappés 1:1 vers `OrchestratorCommand` et servis par le **même** `dispatch` (les trois portes : fichier, MCP, UI) : `idea_list_agents`, `idea_ask_agent`, `idea_reply`, `idea_launch_agent`, `idea_stop_agent`, `idea_update_context`, `idea_create_skill` (7 base) · `idea_context_read`, `idea_context_propose`, `idea_memory_read`, `idea_memory_write` (4 FileGuard C7) · `idea_skill_read` (skill-awareness) · `idea_workstate_read`, `idea_workstate_set` (LS4 ; `set` n'écrit que la ligne de l'agent courant, via l'identité handshake).
### 18.6 Invariant « 1 agent = 1 session vivante » (livré)
- Registres `TerminalSessions` + `StructuredSessions` (`crates/application/src/terminal/registry.rs`) agrégés en `LiveSessions` (PTY+structuré). `session_for_agent` (singulier, **non ambigu**) **+** `sessions_for_agent` (pluriel). Garde reattach `Rebind`/`Refuse`/`Idempotent` dans `LaunchAgent` (`lifecycle.rs`). Ancienne ambiguïté `session-registry-agent-ambiguity` = **fermée par construction**.
---
## 19. Cadrage — couche de persistance conversationnelle + handoff cross-profile incrémental (chantier à découper, PAS d'implémentation)
## 19. Persistance conversationnelle + handoff cross-profile incrémental (cadrage — LIVRÉ P1→P8 + LS1→LS7)
> **Cadrage architecture** (le prochain chantier prioritaire après robustesse : **persistance/reprise → handoff cross-profile**). Produit les **ports**, les **adapters**, les **frontières** et un **découpage en lots testables**. **Aucun code applicatif ici** : la doc est livrable, les lots seront confiés aux binômes Dev/Test (cycle §3).
> **⚠️ STATUT : LIVRÉ (clôture programme live-state/persistance, @ `fd7adbb`).** Ce qui suit était le **cadrage** ; il est désormais **implémenté** (P1→P8 du handoff, puis LS1→LS7 : live-state, borne `summary_md`, rotation/pagination, viewer). La **cartographie de clôture** (4 stores, flux d'injection, chemin chaud vs froid, ce qui reste ouvert) fait foi en **§21** et dans [`docs/LS8-live-state-persistence-closure.md`](docs/LS8-live-state-persistence-closure.md). Précisions sur l'état réel : (1) **P10 (`LlmHandoffSummarizer`)** reste **non activé** — le défaut runtime est l'heuristique borné (LS5, ADR `docs/adr/LS5-handoff-summary-bound-and-llm-seam.md`) ; (2) la **borne `summary_md`** (LS5 : `HANDOFF_SUMMARY_MAX_CHARS=4096`, `bound_handoff_summary`, `TURN_LINE_MAX_CHARS=240`) est appliquée à l'écriture **et** à l'injection ; (3) la **rotation/rétention** du `log.jsonl` (LS6 : archive segmentée, port `ConversationArchive`, lecture paginée, invariant INV-LS6) et le **live-state** (LS1→LS4) ont été ajoutés **au-delà** de ce cadrage initial. Le texte ci-dessous est conservé comme **genèse** ; en cas de divergence, **§21 + `docs/LS8` font foi**.
### 19.0 Problème & objectif produit
Aujourd'hui la continuité d'une conversation repose sur le **`resumable_id` CLI** (`Conversation.resumable_id`, profil `SessionStrategy{assign_flag,resume_flag}`) : au redémarrage on **rejoue la session du provider** (`--resume <id>`). Deux trous :
@ -2345,4 +2347,32 @@ pub enum ScheduledTask {
---
## 21. Clôture du programme live-state / persistance (LS1→LS7) — cartographie des 4 stores
> **Référence durable de clôture** (programme livré @ `fd7adbb`). Détail complet, acquis lot par lot et points ouverts : [`docs/LS8-live-state-persistence-closure.md`](docs/LS8-live-state-persistence-closure.md). §21 fait foi sur la séparation des stores et le flux d'injection.
### 21.1 Quatre stores disjoints (frontière gravée)
| Store | Fichier(s) | Nature | Surface | Versionné | Injecté agent |
|---|---|---|---|---|---|
| Mémoire projet | `.ideai/memory/*.md` + `MEMORY.md` | Savoir stable, curé, low-noise | Agent + humain | Oui | Oui (index/hooks — pointeurs) |
| Handoff | `.ideai/conversations/<id>/handoff.md` | Reprise par fil = dérivé distillé **borné** (≤4096) | Agent (distillé) | Non | Oui (`# Reprise de la conversation`, borné LS5) |
| Transcript | `.ideai/conversations/<id>/log.jsonl` (+ `log.N.jsonl`) | Journal append-only **riche**, source de vérité | **Humain seul** | cf. point ouvert | **JAMAIS** |
| Live-state | `.ideai/live-state.json` | Coordination transitoire **maigre**, keyed LWW, prune TTL+max | Agent (maigre) + humain | Non (gitignoré) | Oui (`# État du projet`, borné LS4) |
**Règle** : surface AGENT = borné/distillé/pointeur ; surface HUMAINE = riche. Le **transcript append-only ne franchit JAMAIS** vers un contexte agent ; seul le **handoff distillé** passe la frontière (borné des deux côtés). Le live-state est keyed last-writer-wins (jamais d'append).
### 21.2 Injection au lancement (ordre `compose_convention_file`)
`# Project root` → `# Orchestration IdeA` (+ capacités) → `# Skills disponibles` → `# Contexte projet` → persona → `# Mémoire projet` (pointeurs) → **`# État du projet`** (live-state lean, LS4) → **`# Reprise de la conversation`** (handoff borné, LS5). Sections vides omises.
### 21.3 Chemin chaud vs froid
- **Chaud** (cheap, jamais ralenti) : `append` (O(1)+fsync), `fold` incrémental + `bound_handoff_summary`, auto-update live-state best-effort sur `ask`/`reply`. **Aucun LLM.**
- **Froid** : rotation `RotateConversationLog` (à la reprise, best-effort, idempotente, **INV-LS6** : jamais d'élagage d'un tour d'id ≥ `up_to`), lecture paginée `read_conversation_page` (viewer humain), `GetLiveStateLean` (prune-on-read).
### 21.4 Reste ouvert
(1) activation réelle du seam LLM (non activé, défaut heuristique, contrat ADR LS5) ; (2) balayage périodique de rotation (idempotent, non câblé) ; (3) discordance D19-4 vs `.gitignore` sur `.ideai/conversations/` (à trancher Git/Main) ; (4) intégration MCP e2e UX ; (5) évolutions multi-fenêtres du registre de sessions ; (6) auto-update mémoire/contexte *en cours* de session. Détail : `docs/LS8` §7.
*Document maintenu par l'Agent Architecture — base du jalon « cadrage architecture » avant tout code applicatif.*