Files
ObsiGate/docs/Assistant-V2/assistant-v21-phase-3-le-monde.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

24 KiB
Raw Permalink Blame History

Assistant V2.1 ObsiGate — Phase 3 : Le monde (connecteurs, synchronisation, client MCP)

Champ Valeur
Phase 3 sur 5 — l'assistant se branche sur les services de Bruno
Document parent ASSISTANT-V2.1.md — en particulier §11 (connecteurs, sync, replis, envoi encadré, client MCP), §9.6 (candidats mémoire), §15 (politiques)
Version 1.0 — 9 octobre 2026
Auteur Spark, pour Bruno
Statut Prêt à implémenter
Tranches #195 (cadre connecteurs + moteur de sync), #196 (Google Calendar + Gmail + replis zéro-OAuth), #197 (Microsoft Teams/Outlook), #198 (Discord lecture + Notion + Drive), #207 (ObsiGate client MCP)
Effort estimé 17–23 jours
Prérequis Phase 0 (politiques #213 — provenance et règles d'envoi ; runs #206), phase 1 (plan et tâches qui consomment l'agenda), phase 2 (bus d'événements #208 qui consomme la sync)
Dépendances #195 ← — ; #196, #197 ← #195, #213 ; #198 ← #195 ; #207 ← #195, #213
Débloque Phase 4 (engagements détectés dans les courriels en mode candidats, préparation de réunions sur agenda réel), phase 5 (packs Communication/Voyages complets)

Résultat visible à la fin de cette phase. Le plan du jour voit le vrai agenda et les vrais courriels. Les connecteurs Google, Microsoft, Discord, Notion et Drive sont installés via le cadre unifié « Sources connectées » : OAuth2 + PKCE quand il le faut, mais aussi des replis sans aucun projet cloud (iCal, CalDAV, IMAP) qui débloquent l'agenda et le courriel dès l'installation. Un moteur de synchronisation incrémentale maintient un état local frais que consomment le plan, les rappels et les automatisations (événements connector.object.* enfin émis). L'assistant cherche et lit le courriel, crée des brouillons, et envoie — uniquement sur approbation renforcée du texte final exact, jamais depuis une tâche planifiée. Enfin, ObsiGate devient client MCP : les serveurs MCP externes de Bruno entrent dans le même registre, sous les mêmes politiques.


Table des matières

  1. Objectif et principe directeur
  2. Périmètre
  3. État d'entrée
  4. Modèle de données
  5. Tickets de travail
  6. API livrée par la phase
  7. Comportements détaillés
  8. Tests et golden tasks
  9. Critères d'acceptation de la phase
  10. Risques spécifiques et vigilance
  11. Ordre d'exécution
  12. Définition de « terminé »

1. Objectif et principe directeur

Le principe : la prise de notes reste le vault ; les connecteurs nourrissent et exécutent, ils ne remplacent rien (hérité de la V2, conservé). La phase ajoute deux corollaires propres à la V2.1 :

  • Un état frais ou un aveu d'état périmé. Le plan, les déclencheurs et les réponses s'appuient sur le cache synchronisé ; si la sync a échoué, l'assistant le dit (« agenda non synchronisé depuis 2 h ») au lieu de planifier sur du vide.
  • L'écriture externe est un privilège d'exécution, pas une capacité de fond. Créer un événement, un brouillon ou envoyer suit les niveaux d'autonomie et la politique de provenance (phase 0) : rien d'externe ne part d'un contenu lu sans validation humaine du résultat final.

2. Périmètre

2.1 Inclus

  • #195 : cadre backend/connectors/ (interface, registre, installations, secrets 0600/env, OAuth2 code + PKCE, refresh transparent, retry/backoff, rate-limit par connecteur, audit de chaque call()) ; migration des sources Gitea/GitHub existantes sous le cadre (même UX) ; UI « Sources connectées » étendue (statut, scopes, test, journal d'appels, budget) ; moteur de sync incrémentale (collections déclarées, curseurs, webhooks fournisseurs quand disponibles sinon polling adaptatif, cache d'objets normalisés, émission des événements bus, page santé des syncs).
  • #196 : Google Calendar (lire/écrire) et Gmail (chercher/lire/brouillon/envoyer selon §7.3) sur le même consentement ; replis zéro-OAuth : iCal (lecture), CalDAV (lecture/écriture), IMAP (lecture courriel) ; guide d'installation Google pas-à-pas FR/EN.
  • #197 : Microsoft Graph — Teams (lire les salons choisis, envoyer en WRITE confirmé) et Outlook (courriel et calendriers, envoi selon §7.3) sur le même jeton Entra ID.
  • #198 : Discord en lecture par bot REST (pas de gateway en v1) en plus du webhook de sortie existant ; Notion (rechercher/lire/appendre, sync vers l'index de recherche) ; Drive (rechercher/lire, import vers le vault).
  • #207 : client MCP (serveurs déclarés par Bruno ; outils préfixés mcp.<serveur>.* dans le registre ; niveau de confiance par serveur ; empreinte de schéma et suspension en cas de changement).

2.2 Exclu (phases ultérieures)

  • La détection d'engagements dans les courriels en production (#211, phase 4) : la phase livre le réglage candidats mémoire (§9.6 du parent, défaut off) et le pipeline ; l'activation métier est en phase 4.
  • Les packs qui consomment ces connecteurs au-delà du socle (Voyages, Communication avancée — phase 5).
  • Slack (option future, inchangé depuis la V2), l'envoi SMTP direct (l'envoi passe par les APIs Google/Microsoft), toute écriture Discord autre que le webhook existant.
  • La visioconférence et l'enregistrement de réunions : phase 4 (#209) pour la préparation et les comptes rendus, sur l'agenda livré ici.

3. État d'entrée

Élément Usage en phase 3
Sources connectées Gitea/GitHub (#92/#103), connected.py, secrets.py Précédent à généraliser ; migration sans perte d'UX
Moteur de politiques + provenance (#213, phase 0) Toute donnée connecteur entre avec connector:<id> ; les règles d'envoi et de sensibilité s'appliquent telles quelles
Runs et approbation renforcée (phase 0/1) Envois et écritures externes sont des steps de runs, approuvés dans la file unifiée
Plan, tâches, automatisations (phases 1–2) Consommateurs de l'agenda synchronisé et des événements connector.object.*
Scheduler amendé (phase 2) Les jobs de sync sont des tâches planifiées système (visibles, journalisées)

4. Modèle de données

-- data/connectors_cache.sqlite3 (reconstructible par re-sync complète)
CREATE TABLE sync_state (
  user_id TEXT NOT NULL, connector_id TEXT NOT NULL, collection TEXT NOT NULL,
  cursor TEXT,                          -- token/page/date du fournisseur
  last_sync_at TEXT, last_error TEXT, status TEXT NOT NULL DEFAULT 'ok',
  PRIMARY KEY (user_id, connector_id, collection)
);
CREATE TABLE synced_objects (
  user_id TEXT NOT NULL, connector_id TEXT NOT NULL,
  external_id TEXT NOT NULL, type TEXT NOT NULL,   -- event|message|chat|page|file
  title TEXT, starts_at TEXT, ends_at TEXT,
  participants_json TEXT, excerpt TEXT,            -- extrait borné, JAMAIS le corps complet
  link TEXT, updated_at TEXT NOT NULL,
  PRIMARY KEY (user_id, connector_id, external_id)
);

Installations et secrets : fichiers existants du patron V2 (data/connectors.json pour le public, data/connector_secrets.json 0600 ou variables OBSIGATE_CONNECTOR_SECRET_<ID> ; jetons OAuth chiffrés avec data/secret.key). Les collections synchronisables et leurs fenêtres (agenda ±30 j ; courriel : dossiers choisis, 14 j glissants ; Teams : salons choisis) sont déclarées par chaque connecteur, réglables dans l'UI.

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. Les fournisseurs sont développés et testés contre des simulateurs (le harnais golden de la phase 0), les appels réels étant vérifiés en recette manuelle documentée par connecteur.

Cadre & sync — #195

P3-T01 — Interface connecteur et registre. Fichiers : backend/connectors/base.py, backend/connectors/registry.py, backend/connectors/oauth.py. Travail : ConnectorInterface (id, label, URL de doc, scopes, capabilities nommées, collections synchronisables) ; installation/désinstallation ; flux OAuth2 code + PKCE générique (endpoints d'allowlist codée par fournisseur) avec redirect /api/connectors/{id}/callback, échange + stockage du refresh token chiffré, refresh anticipé et 1 retry sur 401 dans call() ; auth par token statique pour les connecteurs qui s'y prêtent ; timeouts httpx 15 s, retry backoff, rate-limit par connecteur, cache GET court (TTL 5 min) pour le contexte de chat. Acceptation : un connecteur de test (fixture OAuth complète) s'installe, se teste, se rafraîchit et se désinstalle de bout en bout ; aucun secret n'apparaît dans les logs ni les retours d'outils (redaction vérifiée).

P3-T02 — UI Sources connectées et migration Gitea/GitHub. Fichiers : backend/routers/connectors.py, page Configurations. Travail : cartes par connecteur (statut, scopes accordés, boutons Connecter/Tester/Déconnecter, journal des derniers appels depuis l'audit, budget) ; migration interne de connected.py : Gitea/GitHub deviennent deux connecteurs du cadre, configurations existantes relues sans ressaisie ; les outils historiques gardent leurs noms. Acceptation : une installation avec Gitea/GitHub configurés « à l'ancienne » fonctionne après migration sans intervention ; la page montre les appels récents des deux sources.

P3-T03 — Moteur de synchronisation incrémentale. Fichiers : backend/connectors/sync.py, jobs scheduler système. Travail : pour chaque installation et collection : sync initiale bornée puis incrémentale par curseur ; réception webhook fournisseur quand le connecteur le déclare (Google/Microsoft), sinon polling adaptatif (défaut 15 min, ralenti la nuit, accéléré à l'approche d'un événement) ; normalisation des objets (§4) ; émission connector.object.created|updated sur le bus de la phase 2 ; erreurs → last_error + statut, jamais d'exception silencieuse ; re-sync complète manuelle depuis l'UI. Acceptation : un événement créé côté fournisseur (simulateur) apparaît dans le cache et émet l'événement bus en moins d'un cycle de polling ; la suppression côté fournisseur retire l'objet du cache (pas de fantôme dans le plan).

P3-T04 — Santé des connecteurs. Travail : agrégation par connecteur (dernière sync, retard, taux d'erreur, quota estimé) exposée dans la carte et au centre de contrôle ; signal de dégradation consommé par le plan et le briefing (le consommateur affiche « données périmées » avec l'âge) ; un connecteur déconnecté retire ses outils du registre présenté au modèle (list_tools filtré par capabilities actives, comme le prévoit la V2 pour Tauri). Acceptation : couper la sync (simulateur en échec) fait apparaître l'âge des données dans le plan du jour suivant, sans bloquer le plan.

Google — #196

P3-T05 — Calendar : lecture et écriture. Travail : capabilities gcal.list_events (READ), gcal.get_event (READ), gcal.create_event (WRITE), gcal.update_event (WRITE), toutes avec post-conditions verify (relecture de l'événement) et inverses (suppression de l'événement créé) ; le plan du jour (phase 1) bascule sa source d'agenda sur le cache synchronisé quand le connecteur est actif ; les blocs validés du plan peuvent être écrits dans l'agenda (option « plan → agenda », désactivée par défaut). Acceptation : « bloque 2 h jeudi pour la présentation » crée l'événement vérifié, visible au plan ; le désactiver (déconnexion) rend le plan à sa source vault sans erreur.

P3-T06 — Gmail : chercher, lire, brouillonner. Travail : gmail.search_messages (syntaxe du fournisseur), gmail.read_message, gmail.create_draft (WRITE, inverse = suppression du brouillon) ; citation systématique dans les réponses (expéditeur, date, lien) ; le contenu lu porte la provenance connector:gmail et ne peut pas, seul, justifier une action externe (politique phase 0 — test dédié). Acceptation : « ai-je une réponse du dentiste ? » répond depuis le cache + lecture à la demande, avec citation ; aucun fait mémoire n'est créé depuis un courriel sans passer par le mode candidats (§9.6, défaut off).

P3-T07 — Envoi encadré. Travail : gmail.send (DANGEROUS) selon le parent §11.4 : brouillon d'abord (T06), carte d'approbation renforcée sur le texte final exact (destinataires, objet, corps ; toute modification régénère la carte), interdiction structurelle depuis tâches planifiées et automatisations (la politique refuse avant même la carte), journalisation en step de run avec inverse = néant affiché. Acceptation : les trois refus sont prouvés par tests (envoi en tâche planifiée ; envoi dont le corps est une copie d'un courriel reçu sans reformulation validée ; envoi après modification du brouillon sans nouvelle carte) ; un envoi approuvé part une fois (idempotence).

P3-T08 — Replis zéro-OAuth : iCal, CalDAV, IMAP. Fichiers : backend/connectors/ical.py, caldav.py, imap.py. Travail : iCal par URL secrète (lecture, sync par le moteur T03) ; CalDAV (lecture + création/màj d'événements, mot de passe d'application comme secret) ; IMAP (lecture : recherche et lecture, collections = dossiers choisis) ; ces connecteurs apparaissent dans la même matrice et la même UI, avec leurs limites affichées (« lecture seule » pour iCal/IMAP). Acceptation : un agenda iCloud/Nextcloud de test est lisible dans le plan sans aucun projet Google/Microsoft ; un compte IMAP de test répond à mail.search en lecture seule (toute tentative d'écriture est absente du registre, pas seulement refusée).

Microsoft — #197

P3-T09 — Graph : socle et Teams. Travail : client Graph minimal (auth Entra ID via le cadre OAuth, pagination, erreurs typées) ; teams.list_chats (READ, salons choisis), teams.read_messages (READ), teams.send_message (WRITE avec confirmation affichant le salon et le texte exact — même régime que l'envoi courriel, adapté : pas de brouillon côté Teams, la carte tient lieu de relecture). Acceptation : lire un salon et y répondre via carte fonctionne sur tenant de test ; un salon non choisi n'est ni synchronisé ni lisible par l'outil.

P3-T10 — Outlook : courriel et calendriers. Travail : outlook.search_messages, outlook.read_message, outlook.create_draft, outlook.send (même ticket de politique que P3-T07 — factoriser, ne pas dupliquer la logique d'approbation), outlook.list_events alimentant le cache d'agenda au même titre que gcal (le plan fusionne les calendriers actifs en signalant la source de chaque événement). Acceptation : un plan peut mélanger événements Google et Outlook sans doublon quand le même événement existe des deux côtés (dédup par titre+horaires signalée en carte, jamais de fusion silencieuse).

Discord, Notion, Drive — #198

P3-T11 — Discord en lecture. Travail : bot token (secret) ; discord.list_messages et discord.search en REST sur les salons choisis ; le webhook de sortie existant (#168) devient la capability discord.send du connecteur (WRITE via notify, pas de doublon de code) ; pas de gateway websocket (limite affichée : pas de temps réel, sync par polling). Acceptation : la recherche dans un salon de test retrouve un message cité ; les salons non choisis sont inaccessibles aux outils.

P3-T12 — Notion : lire, appendre, synchroniser la recherche. Travail : API key (token interne) ; notion.search, notion.read_page, notion.append_blocks (WRITE confirmé) ; les pages choisies sont synchronisées vers l'index de recherche d'ObsiGate (elles deviennent trouvables par la recherche et le recall, marquées connector:notion — jamais copiées dans le vault sans demande explicite d'import). Acceptation : « cherche dans Notion et dans mes notes » répond des deux sources distinguées ; un append est vérifié par relecture.

P3-T13 — Drive : chercher, lire, importer. Travail : drive.search, drive.read_file (Docs/Sheets convertis en texte/markdown via l'API) ; import explicite vers le vault (drive.read_file → proposition de note, propose/apply existant) rattaché au backlog d'import ; lecture seule stricte (aucune écriture Drive cette phase). Acceptation : importer un document Drive crée une note du vault citant sa source ; l'original Drive n'est jamais modifié.

Client MCP — #207

P3-T14 — Branchement de serveurs MCP externes. Fichiers : backend/connectors/mcp_client.py. Travail : déclaration de serveurs (URL/transport, authentification par secret) ; découverte des outils à la connexion ; enregistrement dynamique préfixé mcp.<serveur>.* avec risque déclaré par le serveur plafonné par la politique (un outil déclaré READ par le serveur mais à effets inconnus démarre au niveau d'autonomie 0–1) ; les contenus MCP portent la provenance mcp:<serveur> ; aucun contenu sensitivity ∈ {private, sensitive} n'est transmissible à un outil MCP externe (politique phase 0, test dédié). Acceptation : un serveur MCP de test expose un outil utilisable en conversation au niveau 1 ; tenter de lui passer un fait sensitive est refusé et journalisé.

P3-T15 — Empreintes de schéma et suspension. Travail : empreinte (hash canonique du schéma de chaque outil) stockée à la validation du serveur ; à chaque session, comparaison : un schéma modifié suspend l'outil (retiré du registre présenté au modèle) et crée une carte de revalidation montrant le diff de schéma ; la revalidation est manuelle, par serveur. Acceptation : modifier le schéma d'un outil du serveur de test suspend exactement cet outil, pas les autres, et l'assistant explique la suspension au lieu d'échouer en silence.

Finitions

P3-T16 — Guides d'installation des fournisseurs. Travail : docs/GUIDES/CONNECTEURS_GOOGLE_MS.md pas-à-pas FR/EN (création des applications Google Cloud et Entra ID, écrans de consentement, URI de redirection) + section replis zéro-OAuth (où trouver l'URL iCal, créer un mot de passe d'application) ; fiches docs/features/ pour #195–#198 et #207 ; clés i18n des Sources connectées et des cartes d'envoi. Acceptation : un lecteur suivant le guide seul aboutit à un connecteur Google testé vert ; DoD du §12 pour le reste.

6. API livrée par la phase

Route Rôle Ticket
GET /api/connectors · `POST /api/connectors/{id}/install test disconnect`
GET /api/connectors/{id}/authorize · GET /api/connectors/{id}/callback Flux OAuth2 + PKCE P3-T01
GET /api/connectors/{id}/health · POST /api/connectors/{id}/sync Santé et re-sync manuelle P3-T03/T04
GET/POST /api/connectors/mcp-servers · POST /api/connectors/mcp-servers/{id}/revalidate Serveurs MCP externes et revalidation P3-T14/T15

Les capabilities connecteurs sont des outils @tool ordinaires (donc exposés au serveur MCP d'ObsiGate sous les mêmes politiques) ; il n'y a pas d'API REST par capability.

7. Comportements détaillés

7.1 Ce que la sync ne fait jamais

Le corps complet d'un courriel n'est jamais synchronisé (extrait borné seulement) ; rien de synchronisé n'entre dans le vault ni dans la mémoire sans le pipeline candidats (§9.6, défaut off) ; désinstaller un connecteur purge son cache et ses curseurs (les secrets sont retirés, l'audit conserve la trace des appels passés sans contenu).

7.2 Fuseaux et calendriers multiples

Les événements synchronisés conservent leur fuseau d'origine ; le plan les projette dans OBSIGATE_TZ en affichant l'heure d'origine quand elle diffère (voyage). Un événement « toute la journée » reste ancré à sa date, jamais converti en minuit UTC décalé.

7.3 L'envoi, en une phrase

Brouillon toujours, envoi jamais sans le texte final sous les yeux, et jamais en automatique : ces trois règles sont codées dans la politique (phase 0) et testées en P3-T07/T10 ; le réglage d'activation de l'envoi (décision ouverte n° 2 du parent) peut le désactiver par connecteur sans toucher au code.

7.4 Tauri et hors-ligne

Sans token installé, les capabilities sont absentes du registre présenté au modèle (pas d'outils fantômes) ; hors-ligne, l'assistant répond depuis le cache synchronisé en annonçant son âge ; aucune tentative réseau n'a lieu au démarrage (P8 du parent).

8. Tests et golden tasks

  • Simulateurs : chaque fournisseur a un double HTTP (réponses figées + scénarios d'erreur : 401, 429, pagination, webhook) utilisé en CI ; aucun test CI ne dépend d'un vrai compte.
  • Recette manuelle : par connecteur, un pas-à-pas de vérification sur compte réel, archivé dans la fiche de tranche (captures expurgées).
  • Golden tasks nouvelles de la phase : G9 — « événement créé côté fournisseur → présent au plan, et plan honnête si la sync tombe » ; G10 — « envoi refusé en tâche planifiée, accepté en interactif sur texte exact » ; G11 — « schéma d'outil MCP modifié → outil suspendu, autres intacts ». (Numérotation à la suite de G1–G8 du parent.)
  • Non-régression : les sources Gitea/GitHub et les canaux de notification existants fonctionnent à l'identique après migration au cadre.

9. Critères d'acceptation de la phase

  1. Google ou un repli zéro-OAuth suffit à alimenter le plan en agenda réel ; la fraîcheur des données est affichée, jamais présumée.
  2. Une recherche de courriel répond avec citation ; un brouillon se crée ; un envoi n'a lieu que sur approbation renforcée du texte final, et jamais en planifié.
  3. Désinstaller un connecteur retire ses outils, purge son cache et ses secrets, et dégrade proprement les consommateurs (plan, briefing).
  4. Un serveur MCP externe fonctionne sous politique ; un changement de schéma suspend l'outil concerné avec une carte de revalidation.
  5. G9, G10 et G11 sont verts en CI sur simulateurs.

10. Risques spécifiques et vigilance

Risque Vigilance
Création des applications OAuth = friction d'installation Les replis zéro-OAuth (P3-T08) débloquent l'usage avant tout projet cloud ; le guide T16 est testé par un lecteur naïf
Quotas et changements d'API des fournisseurs Tout passe par call() (rate-limit, backoff, audit) ; les échecs de sync dégradent, ne cassent pas (§7, P3-T04)
Double agenda = double vérité Le cache distingue les sources ; la dédup d'affichage est signalée, jamais silencieuse (P3-T10)
Un connecteur compromis devient un canal d'injection La provenance connector:<id> et les règles de la phase 0 s'appliquent à 100 % du contenu sync et lu ; les extraits synchronisés sont traités comme des données, jamais des instructions

11. Ordre d'exécution

P3-T01 → T02 → T03 → T04        (#195 — la sync avant les connecteurs qui s'en servent)
P3-T05 → T06 → T07 ; T08        (#196 — T08 indépendant, parallélisable après T01)
P3-T09 → T10                    (#197 — T10 réutilise la politique d'envoi de T07)
P3-T11 ; T12 ; T13              (#198 — parallélisables entre eux)
P3-T14 → T15                    (#207 — après T01 et la politique phase 0)
P3-T16                          (clôture)

12. Définition de « terminé »

DoD du document de phase 0 §12, inchangée, plus : recette manuelle sur compte réel archivée par connecteur ; toute capability d'écriture déclare sa post-condition et son inverse (exigence du parent §21) ; les golden tasks G9–G11 vertes en CI.