Files
ObsiGate/docs/Assistant-V2/assistant-v21-phase-0-socle-sur.md
T
bruno f783394f09
CI / lint (push) Successful in 3m17s
CI / security (push) Failing after 1m51s
CI / test (push) Successful in 5m14s
CI / build (push) Successful in 1m50s
CI / e2e (push) Successful in 18m18s
feat: intégration TikTak #197-200 + scoped tokens, webhooks filtrés, listes mtime/size ; fix e2e-server BUG-112
2026-10-14 11:42:53 -04:00

279 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Assistant V2.1 ObsiGate — Phase 0 : Socle sûr (purge, mémoire, politiques, exécution fiable)
| Champ | Valeur |
|---|---|
| Phase | **0 sur 5** — fondation de toute la solution |
| Document parent | `ASSISTANT-V2.1.md` — en particulier §2 (P1–P16), §5, §9, §10, §15, §16, §19 |
| Version | 1.0 — 9 octobre 2026 |
| Auteur | Spark, pour Bruno |
| Statut | Prêt à implémenter |
| Tranches | **#191** (purge Ollama), **#192** (mémoire socle amendée), **#193** (miroir markdown), **#213** (politiques & taint), **#206** (exécution fiable), amorce de **#212** (harnais golden tasks) |
| Effort estimé | 15–19 jours (dont 1–2 j d'amorce #212 ; le solde de #212 est continu, phases 1–5) |
| Prérequis | ObsiGate actuel : boucle agent `backend/agent/loop.py`, registre `backend/tools/registry.py`, scheduler #170, notifications #168, Web Push #67, recherche #70 — tels que décrits dans l'inventaire V2.1 §4 |
| Dépendances | Aucune (première phase) |
| Débloque | Phase 1 (tâches et planification s'appuient sur les runs et la mémoire), et toutes les écritures externes ultérieures (#213/#206 sont le prérequis de sécurité des phases 3–4) |
> **Résultat visible à la fin de cette phase.** Le conteneur Ollama a disparu. L'assistant possède une mémoire long terme : il enregistre des faits (avec historique bi-temporel), des entités (personnes, projets, lieux) et des procédures, les montre dans `99_Assistant/Profil.md` — éditable à la main, les éditions étant reprises comme des faits de confiance maximale — et les injecte dans chaque conversation via un compileur de contexte dont chaque section cite son origine. Surtout, **tout ce que l'assistant exécute devient un run persistant** : plan affiché avant les écritures, approbation par run, étapes journalisées avec preuves de post-condition, reprise sans doublon après interruption, annulation quand l'inverse existe. Et un moteur de politiques déterministe décide ce qui est autorisé, approuvé ou refusé — jamais le modèle.
---
## Table des matières
1. [Objectif et principe directeur](#1-objectif-et-principe-directeur)
2. [Périmètre](#2-périmètre)
3. [État d'entrée — ce qui existe](#3-état-dentrée--ce-qui-existe)
4. [Modèle de données](#4-modèle-de-données)
5. [Tickets de travail](#5-tickets-de-travail)
6. [API livrée par la phase](#6-api-livrée-par-la-phase)
7. [Comportements détaillés](#7-comportements-détaillés)
8. [Tests et golden tasks](#8-tests-et-golden-tasks)
9. [Critères d'acceptation de la phase](#9-critères-dacceptation-de-la-phase)
10. [Risques spécifiques et vigilance](#10-risques-spécifiques-et-vigilance)
11. [Ordre d'exécution](#11-ordre-dexécution)
12. [Définition de « terminé »](#12-définition-de--terminé-)
---
## 1. Objectif et principe directeur
Le principe : **on ne donne pas plus de pouvoir à l'assistant avant d'avoir rendu son pouvoir actuel sûr, traçable et réversible.** La V2 livrait la mémoire puis les connecteurs ; la V2.1 intercale, dès la phase 0, les deux mécanismes qui rendent toutes les phases suivantes délégables : le **moteur de politiques** (#213 — qui a le droit de faire quoi, avec quelles données, venues d'où) et l'**exécution fiable** (#206 — plan, preuves, reprise, annulation).
Corollaires qui guident chaque ticket :
- **La mémoire est un index du vault, pas un silo.** Faits confirmés → miroir markdown ; la base ne détient en propre que les états de travail (`pending`, historique de runs).
- **Un run est l'unité de tout.** Conversation, tâche planifiée (phase 2), automatisation (phase 2), MCP : à la fin de la phase, toute exécution d'outils par un agent produit une ligne `agent_runs` et ses `agent_steps`. Les journaux épars d'aujourd'hui convergent là.
- **La politique est déterministe.** Risque du registre × niveau d'autonomie × provenance × sensibilité × réversibilité → décision. Aucun appel LLM dans le chemin d'autorisation, jamais.
- **Pas de succès sans preuve.** Un run n'est `succeeded` que si ses post-conditions vérifiables sont vraies (§7.3).
## 2. Périmètre
### 2.1 Inclus
- #191 : retrait du service/volume Ollama des trois compose, défauts provider nettoyés, notes de migration (repris tel quel de la V2 §9).
- #192 : `backend/memory/` — store SQLite amendé (faits bi-temporels, sensibilité, provenance, procédures), entités et relations, recall et **compileur de contexte** (§9.4 du parent), outils `memory.*`, `entity_resolve`, `procedure_get`, API mémoire, cartes de validation des faits `pending` (forme simple — la file unifiée arrive en phase 1, #203).
- #193 : `mirror.py`, dossier `99_Assistant/`, `Profil.md` à balises gérées avec re-parse des éditions humaines, `Journal.md` des consolidations, `Personnes.md` en **coquille** (le peuplement riche est en phase 4, #211).
- #213 : provenance/taint sur tous les contenus entrant dans un contexte ou un run, moteur de politiques `backend/agent/policies.py` évalué avant chaque appel d'outil, règles du §15.2 du parent, journal des refus, tests d'injection.
- #206 : plans d'exécution, `agent_runs`/`agent_steps` dans `data/assistant.sqlite3`, idempotence et reprise, post-conditions déclarées dans le registre (`verify`), inverses (`inverse`), niveaux d'autonomie 0–3 et délégations bornées datées, API runs + progression SSE.
- #212 (amorce) : harnais de golden tasks (`tests/golden/`, fixtures de vault, connecteurs simulés) et les scénarios G3, G6, G7 de la phase.
### 2.2 Exclu (phases ultérieures)
- La **consolidation nocturne** (#194) : la phase 0 livre le store, le recall et la création de faits en conversation ; l'extraction automatique nocturne arrive en phase 2, quand `agent_prompt` (#200) existe.
- La **vue Aujourd'hui**, la file d'approbations unifiée et le centre de contrôle complet (#203, phase 1) : la phase 0 livre les cartes mémoire simples et une liste de runs basique dans les Configurations.
- Les **tâches** (#202, phase 1) et tout connecteur (#195+, phase 3).
- La politique « contenu connecteur » est livrée comme **mécanisme** (provenances `connector:<id>` reconnues) ; son premier usage réel est en phase 3.
## 3. État d'entrée — ce qui existe
| Élément | État | Rôle en phase 0 |
|---|---|---|
| Boucle agent + tool calling natif | ✅ `backend/agent/loop.py`, `backend/ai_chat.py` | Moteur des runs ; étendu, pas remplacé (P2) |
| Registre `@tool` (risque/scope/confirmation/audit/rate-limit) | ✅ `backend/tools/registry.py` | Reçoit les champs `verify` et la provenance ; la politique s'y branche |
| Historique de conversation persistant | ✅ #95, `backend/ai_history.py` | Mémoire épisodique brute (source future de #194) |
| Recherche TF-IDF + sémantique RRF | ✅ #70 | Recall : fusion faits + recherche |
| Cache SQLite (patron) | ✅ `backend/tools/webcache.py` | Modèle des stores SQLite de la phase |
| Audit + redaction des secrets | ✅ `backend/audit.py`, `secret_redactor.py` | Journal des runs et des refus ; args toujours redactés |
| Providers LLM multi-fournisseurs | ✅ `backend/ai.py` | Routage par type de job : amorce posée (§16.3 du parent), table complète en phases suivantes |
| Ollama comme conteneur | ✅ présent | **Retiré** par #191 ; le provider `ollama` reste une option de configuration (desktop local) |
## 4. Modèle de données
Repris du parent : `facts` amendée (§9.2 — `entity_id`, `valid_from`, `valid_to`, `recorded_at`, `sensitivity`, `provenance_trust`, `kind` étendu à `procedure`), `entities` et `relations` (§9.3), `consolidation_runs` (créée dès cette phase, alimentée en phase 2), `agent_runs` et `agent_steps` (§10.2), auxquelles s'ajoute la table des délégations :
```sql
CREATE TABLE delegations (
id TEXT PRIMARY KEY, user_id TEXT NOT NULL,
domain TEXT NOT NULL, -- 'vault'|'tasks'|'email'|'calendar'|…
level INTEGER NOT NULL, -- niveau accordé (0–3, §10.4 du parent)
scope_json TEXT, -- périmètre : chemins, connecteur, budgets
granted_at TEXT NOT NULL, review_at TEXT NOT NULL, -- défaut +90 j
status TEXT NOT NULL DEFAULT 'active' -- active|expired|revoked
);
```
Stores : `data/memory.sqlite3` (faits, entités, relations, runs de consolidation) et `data/assistant.sqlite3` (runs, steps, délégations ; recevra `inbox_items`, `commitments`, `approvals` en phases 1 et 4 — le fichier est créé ici, les tables arrivent avec leurs tranches).
Règles transverses : toute table porte `user_id` ; aucun secret ni contenu brut de connecteur dans ces bases ; les args d'étapes sont stockés **redactés** ; les stores sont reconstructibles ou journalisés (jamais de contenu de Bruno irremplaçable hors du vault — P4).
## 5. Tickets de travail
*Convention : chaque ticket est indépendamment testable ; « Fichiers » donne les points d'ancrage d'après la V2.1 — à confirmer contre le code réel au démarrage du ticket.*
### Purge Ollama — #191
**P0-T01 — Retrait du conteneur Ollama.**
Fichiers : `docker-compose.yml`, `docker-compose.test-linux.yml`, `docker-compose.test-win.yml`, docs d'installation.
Travail : supprimer service, volume `ollama_data`, variables `OLLAMA_BASE_URL`/`OLLAMA_MODEL` et mentions du port 11434 ; procédure de suppression du volume documentée dans le CHANGELOG.
Acceptation : `docker compose config` ne connaît plus Ollama ; l'instance démarre et l'agent fonctionne sur deepseek/openrouter/gemini.
**P0-T02 — Défauts provider nettoyés.**
Fichiers : `backend/ai.py`, `model_capabilities.py`, tests d'isolation.
Travail : conserver le provider `ollama` comme option explicite (desktop), retirer tout défaut pointant vers le réseau interne ; le mode par défaut de la mémoire fonctionne sans embeddings (TF-IDF + RRF + faits).
Acceptation : aucun test ni défaut ne référence `http://ollama:11434` ; la recherche sémantique se dégrade proprement sans sentence-transformers.
### Mémoire socle — #192
**P0-T03 — Store mémoire : faits amendés, entités, relations.**
Fichiers : nouveau `backend/memory/store.py` (patron `webcache.py`), création/ouverture de `data/memory.sqlite3`.
Travail : DDL du §4 ; CRUD faits avec clôture bi-temporelle (supersede = `valid_to`, jamais d'écrasement) ; planchers de confiance V2 (< 0,7 → `pending`) ; dossiers sensibles configurables → `pending` + `sensitivity='sensitive'` ; renforcement `last_seen_at` et decay par `kind` (les `procedure` et `decision` ne décroissent pas).
Acceptation : G7 passe (changement de dentiste : ancien fait clos, nouveau actif, requête historique datée correctement répondue) ; aucun fait `sensitive` créé directement en `confirmed`.
**P0-T04 — Entités et résolution de noms.**
Fichiers : `backend/memory/entities.py`.
Travail : CRUD entités (person/place/project/org/topic, alias) ; `resolve(name)` → candidates scorées ; **ambiguïté = pas de fusion** (renvoyer les candidates, la décision est une carte, phase 2 pour la consolidation) ; relations bi-temporelles.
Acceptation : deux « Marie » distinctes coexistent sans fusion ; une relation fermée n'apparaît plus dans le recall actuel mais reste interrogeable à date.
**P0-T05 — Recall et compileur de contexte.**
Fichiers : `backend/memory/recall.py`, `backend/memory/compiler.py`.
Travail : score explicite et journalisé (RRF × confiance × fraîcheur × boost entité, parent §9.4) ; assemblage du bloc `<assistant-context>` à budget adaptatif (défaut 2 500 tokens) avec sections profil/procédures, entités du sujet, faits liés, engagements (vide cette phase), dernier briefing (absent cette phase — section omise proprement) ; chaque section cite son origine ; le bloc est visible dans les étapes du panneau avec le détail du score au survol.
Acceptation : un fait sous le seuil n'entre pas dans le bloc ; le même bloc est reproduit à l'identique pour la même requête et le même état (déterminisme de structure) ; aucun contenu `sensitivity='sensitive'` dans un contexte destiné à un outil externe.
**P0-T06 — Outils mémoire et API.**
Fichiers : `backend/tools/memory.py`, `backend/routers/memory.py`.
Travail : les 5 outils V2 (`memory_recall`, `memory_save`, `memory_update`, `memory_forget`, `profile_get`) + `entity_resolve` (READ) et `procedure_get` (READ) ; `memory_save` passe par la politique (approbation selon niveau, défaut 2) ; endpoints CRUD faits (liste/filtre statut, validation `pending`, édition, oubli) et export JSON complet du domaine mémoire.
Acceptation : un fait proposé en chat apparaît en carte, validé il est au miroir (T08) et au recall suivant ; `memory_forget` le retire du recall et du miroir, avec trace d'audit.
### Miroir markdown — #193
**P0-T07 — Miroir `Profil.md` à balises gérées.**
Fichiers : `backend/memory/mirror.py`.
Travail : création de `99_Assistant/` dans le vault mémoire (`OBSIGATE_MEMORY_VAULT`, mode multi-utilisateur du §5.3 du parent) ; rendu des sections gérées (préférences, procédures, objectifs connus) ; hash de section stocké en base ; écritures idempotentes (rendu deux fois = fichier identique).
Acceptation : supprimer un fait validé régénère le miroir sans toucher au hors-balises ; deux rendus consécutifs produisent un diff vide.
**P0-T08 — Re-parse des éditions humaines.**
Travail : avant chaque écriture, comparer le hash stocké au contenu courant ; une section modifiée à la main est re-parsée en faits `source='user'`, confiance 1.0, `confirmed`, puis régénérée ; une suppression dans le miroir = fait `rejected` en base ; le hors-balises n'est jamais touché.
Acceptation : éditer `Profil.md` à la main (ajout + suppression) puis déclencher un rendu donne des faits conformes aux éditions, sans perte du texte libre hors balises.
**P0-T09 — `Personnes.md` (coquille) et `Journal.md`.**
Travail : `Personnes.md` rendu depuis les entités personnes (nom, type de relation, dernière interaction connue) — en lecture structurée simple cette phase ; `Journal.md` alimenté par les événements mémoire (faits ajoutés/validés/rejetés, rendus du miroir) en lecture seule.
Acceptation : une entité personne créée en T04 apparaît au miroir ; le journal trace chaque validation de carte.
### Politiques & taint — #213
**P0-T10 — Provenance de bout en bout.**
Fichiers : `backend/agent/loop.py`, `backend/tools/registry.py`, `backend/ai_chat.py`.
Travail : chaque contenu entrant dans un contexte ou un résultat d'outil reçoit une provenance (`user`, `vault`, `connector:<id>`, `web`, `mcp:<serveur>`, `automation`) portée dans les messages et les steps ; les résultats d'outils sont sanitisés (redaction existante) et étiquetés avant retour au modèle ; les prompts système distinguent explicitement blocs « instructions » et blocs « données ».
Acceptation : un test d'intégration montre la provenance présente sur chaque résultat d'outil d'un run ; aucun résultat n'est injecté sans étiquette.
**P0-T11 — Moteur de politiques.**
Fichiers : nouveau `backend/agent/policies.py` ; branchement dans le registre (évaluation **avant** chaque appel d'outil).
Travail : décision déterministe `autorisé | approbation_run | approbation_renforcée | refusé` = f(risque registre, niveau d'autonomie du domaine, provenance, sensibilité des données, réversibilité) ; règles du parent §15.2 (une instruction de contenu non-`user` ne crée/lève aucune approbation ; action externe justifiée uniquement par un contenu non-`user` → refus + carte ; données `private|sensitive` interdites vers web/connecteurs en écriture/MCP externe) ; journal des refus avec motif, lisible dans l'audit.
Acceptation : G3 passe (courriel piégé simulé → refus, carte à Bruno, zéro appel d'envoi) ; la décision est couverte par des tests unitaires exhaustifs sur la matrice des facteurs (aucun LLM dans le chemin).
**P0-T12 — Registre étendu : `verify`, inverses, sensibilité des paramètres.**
Fichiers : `backend/tools/registry.py`, outils existants (annotations).
Travail : champs optionnels du décorateur `@tool` : `verify` (post-condition vérifiable), `inverse` (constructeur d'inverse ou `None` explicite), `data_classes` (classes de données acceptées en entrée) ; annoter les outils vault et documents existants ; un outil d'écriture sans `verify` déclaré est listé dans un rapport de couverture (pas bloquant cette phase, bloquant pour les outils externes des phases 3+).
Acceptation : le rapport de couverture existe et est revu ; `create_file`/`edit_file` déclarent leur post-condition et leur inverse (backup).
### Exécution fiable — #206
**P0-T13 — Stores de runs.**
Fichiers : `backend/agent/runs.py`, `data/assistant.sqlite3`.
Travail : DDL `agent_runs`/`agent_steps` du parent §10.2 ; cycle de vie complet des statuts ; écriture des steps au fil de l'exécution par la boucle existante (toute exécution d'outils crée ou rattache un run, `origin` renseignée) ; plafonds : itérations et budget tokens existants + durée murale et nombre d'écritures (`OBSIGATE_RUN_MAX_WALL_SECONDS`, `OBSIGATE_RUN_MAX_WRITES`).
Acceptation : tuer le processus pendant un run laisse un run `running` orphelin détectable au redémarrage (marqué `paused` reprenable, jamais `succeeded`).
**P0-T14 — Plans d'exécution et approbation par run.**
Travail : pour toute demande ≥ 2 écritures ou 1 outil DANGEROUS, génération d'un plan (étapes : outil, args résumés, effet attendu, post-condition, inverse) persisté dans `plan_json` ; affichage dans le panneau avant la première écriture ; validation en un clic = approbation du run entier (pas par étape) ; un plan refusé n'exécute rien et le run passe `cancelled` avec motif.
Acceptation : une demande multi-écritures ne produit aucun effet avant validation ; le plan affiché correspond exactement aux steps exécutés ensuite.
**P0-T15 — Idempotence et reprise.**
Travail : `idempotency_key` par step d'écriture (hash run+seq+args) respectée par les outils de création (vérification d'existence par clé en métadonnée) ; reprise d'un run interrompu à la première step non `done`, re-vérification des post-conditions incertaines, steps WRITE incertaines → `awaiting_approval` plutôt que rejeu aveugle.
Acceptation : G6 passe (run tué à mi-parcours, repris : aucun doublon créé, état final vérifié).
**P0-T16 — Vérification et annulation.**
Travail : exécution des `verify` après chaque step d'écriture ; statut final `succeeded` seulement si toutes les post-conditions vérifiables sont vraies, sinon `partially_succeeded` détaillé ; « Annuler ce run » exécute les inverses sûrs en ordre décroissant comme un run lié ; absence d'inverse affichée comme telle (« non annulable »).
Acceptation : un outil dont la post-condition échoue (fixture) produit `partially_succeeded`, jamais `succeeded` ; l'annulation d'un run de création de fichiers restaure l'état initial via les backups.
**P0-T17 — Niveaux d'autonomie et délégations.**
Fichiers : réglages Configurations, `delegations` (§4).
Travail : niveaux 0–3 par domaine (défauts : vault = 2, tout domaine externe = 0/1 selon existence) ; création de délégation bornée (périmètre + budget + `review_at` +90 j) depuis une approbation (« toujours autoriser ce périmètre ») ou les Configurations ; expiration automatique → retour au niveau inférieur ; **aucun niveau ne couvre** paiements, envois externes sans relecture, suppressions définitives, changements de sécurité (codé en politique, pas en réglage).
Acceptation : une délégation expirée cesse de s'appliquer sans intervention ; tenter de déléguer un envoi externe est refusé par l'UI et par l'API.
**P0-T18 — API runs et progression.**
Fichiers : `backend/routers/runs.py`.
Travail : `GET /api/runs` (filtres statut/origine), `GET /api/runs/{id}` (plan, steps, preuves), actions `pause`/`resume`/`cancel`/`undo` ; progression en SSE sur le flux existant ; liste de runs basique dans les Configurations (le centre de contrôle complet est en phase 1).
Acceptation : un run en cours est observable en direct ; pause puis reprise depuis l'API aboutit au même résultat qu'une exécution continue.
### Évaluation — amorce #212
**P0-T19 — Harnais golden tasks.**
Fichiers : `tests/golden/` (fixtures de vault, connecteurs simulés, harnais d'exécution headless).
Travail : infrastructure de scénarios rejouables en CI (état initial → demande → assertions sur les stores, le vault et les runs) ; livrer G3 (injection), G6 (reprise), G7 (bi-temporalité) ; gabarit documenté pour les phases suivantes (chaque phase ajoute les siens).
Acceptation : les trois scénarios tournent en CI et échouent si le comportement régresse (démontré par mutation volontaire en développement).
### Finitions
**P0-T20 — i18n, docs, guides.**
Travail : clés FR/EN pour tout libellé nouveau (`ai.step.*` mémoire/runs/politiques, cartes, réglages d'autonomie) ; fiches `docs/features/` pour #191, #192/#193, #213, #206 ; guide utilisateur « Mémoire et contrôle de l'assistant » ; CHANGELOG + ROADMAP.
Acceptation : DoD du §12 vérifiée point par point.
## 6. API livrée par la phase
| Route | Rôle | Ticket |
|---|---|---|
| `GET/POST/PATCH /api/memory/facts` | CRUD faits, validation des `pending` | P0-T06 |
| `GET /api/memory/entities` · `/api/memory/export` | Entités ; export du domaine mémoire | P0-T04/T06 |
| `GET /api/runs` · `GET /api/runs/{id}` | Liste et détail (plan, steps, preuves) | P0-T18 |
| `POST /api/runs/{id}/pause|resume|cancel|undo` | Pilotage d'un run | P0-T18 |
| `GET/POST/DELETE /api/delegations` | Délégations bornées par domaine | P0-T17 |
Toutes suivent l'authentification et les permissions par vault existantes ; les nouveaux outils `@tool` sont exposés au MCP automatiquement (scopes IN_APP+MCP, propose/apply déjà gérés).
## 7. Comportements détaillés
### 7.1 Qui peut faire quoi
La mémoire, les runs et les délégations sont **par utilisateur** (`user_id` partout, parent §5.3). Un administrateur ne voit pas la mémoire d'un autre utilisateur ; l'audit montre les refus de politique sans le contenu sensible qui les a motivés.
### 7.2 Fait `pending` : durée de vie
Un fait `pending` reste en carte jusqu'à action (valider/éditer/oublier) — pas d'expiration silencieuse. Au-delà de 30 jours, il est regroupé dans un rappel hebdomadaire unique (pas une notification par fait ; le budget d'interruption P15 s'applique dès cette phase aux cartes).
### 7.3 Post-conditions : la règle du succès
`verify` absent ≠ succès présumé : le step est marqué `verified = NULL` et le run ne peut prétendre qu'à `succeeded` si **aucun** step d'écriture n'est simultanément `done` et non vérifié *alors qu'un `verify` existait*. Le rapport de couverture (P0-T12) rend la dette visible au lieu de la cacher.
### 7.4 Reprise après crash du conteneur
Au démarrage, tout run `running` sans heartbeat récent passe `paused` avec le motif `interrupted` ; la reprise est manuelle (API ou panneau) cette phase — la reprise automatique des runs `origin='scheduled'` arrive avec #200 en phase 2.
## 8. Tests et golden tasks
- Unitaires : store mémoire (clôture bi-temporelle, decay, planchers), résolution d'entités (ambiguïté), moteur de politiques (matrice complète des facteurs), idempotence (rejouer une step ne crée pas de doublon), miroir (idempotence du rendu, re-parse).
- Intégration : run complet avec plan → approbation → exécution → vérification ; kill en cours de run → reprise ; édition manuelle de `Profil.md` → faits `source='user'`.
- Golden tasks de la phase : **G3** (injection), **G6** (reprise), **G7** (bi-temporalité) — définitions au parent §16.1.
- Non-régression : l'assistant actuel (sans mémoire activée) se comporte à l'identique ; `OBSIGATE_MEMORY_ENABLED=false` éteint la phase proprement.
## 9. Critères d'acceptation de la phase
1. Le conteneur Ollama n'existe plus et l'assistant fonctionne sur les providers API.
2. Un fait dit en conversation, validé par carte, est dans `Profil.md`, au recall de la conversation suivante, et son remplacement ultérieur conserve l'historique interrogeable à date.
3. Éditer `Profil.md` à la main modifie la mémoire (source `user`, confiance 1.0) ; rien hors balises n'est jamais écrasé.
4. Un run multi-écritures affiche son plan, n'exécute rien avant approbation, prouve chaque post-condition, et un run tué reprend sans doublon.
5. Le scénario d'injection G3 est refusé par la politique, journalisé, et présenté à Bruno en carte.
6. Les golden tasks G3/G6/G7 sont verts en CI.
## 10. Risques spécifiques et vigilance
| Risque | Vigilance |
|---|---|
| Le compileur de contexte consomme le budget du modèle en silence | Le bloc est plafonné, son détail est visible (P0-T05) ; la métrique « tokens de contexte mémoire / conversation » est journalisée dès cette phase (alimente #212) |
| Approbation par run trop grossière (tout ou rien) | Le plan affiche chaque step ; un step peut être **exclu** du plan avant validation (case par step) — l'approbation reste unique, le périmètre est ajustable |
| Le miroir et une édition collaborative (Yjs) se marchent dessus | Le miroir écrit via le même chemin de sauvegarde que l'éditeur (backup préalable) et compare les hashs avant/après (règle durcie en phase 2 pour la consolidation) |
| Dérive du périmètre : vouloir les tâches dès la phase 0 | Les tâches sont la phase 1 ; la phase 0 ne livre que les fondations qu'elles consommeront (runs, politique, mémoire) |
## 11. Ordre d'exécution
```
P0-T01 → T02 (#191, indépendant, à faire en premier)
P0-T03 → T04 → T05 → T06 (#192)
P0-T07 → T08 → T09 (#193, après T03/T06)
P0-T10 → T11 → T12 (#213, T10 tôt : la provenance sert les runs)
P0-T13 → T14 → T15 → T16 → T17 → T18 (#206, après T11)
P0-T19 (harnais tôt, scénarios au fil des tickets) → P0-T20 (clôture)
```
## 12. Définition de « terminé »
DoD V2 §13 (tests unitaires + non-régression, JSDOM si UI, ruff/mypy 0 erreur, i18n FR/EN, CHANGELOG, ROADMAP, fiche par tranche, guide si exposé, CI verte, commit conventionnel référençant l'ID) **plus** les ajouts V2.1 (parent §21) : golden tasks du périmètre vertes ; toute écriture déclare sa post-condition et son inverse ou son absence ; toute source de contenu déclare sa provenance.