Files
flowdeck/docs/V71_Calendar_Meetings.md
bruno 0218d8f5e5
FlowDeck CI / lint (push) Successful in 1m37s
FlowDeck CI / test (push) Failing after 24m19s
FlowDeck CI / docker (push) Skipped
fix: /local-workspace — chemin du header + clic sur un dossier du sidebar (v7.69.9)
Corrige deux régressions de /local-workspace :

- le chemin du header restait bloqué sur « Home / <workspace> » quel que
  soit le dossier affiché : la route rend désormais breadcrumb_items
  (Home / <workspace> / <dossier> / <sous-dossier>, niveaux cliquables,
  collapse « … » au-delà de 4) et la navigation sans rechargement recalcule
  le chemin via l'event flowdeck:breadcrumb-changed ;
- le clic sur un dossier du sidebar affichait TOUS les composants à la
  fois : Alpine.data('wsInitData') retournait le même objet singleton, le
  2e montage (navigation partielle) levait « Cannot redefine property:
  \ » et initTree abandonnait, laissant tout le contenu au state
  brut. La factory retourne désormais une enveloppe fraîche par montage
  qui délègue à l'état réactif partagé. #lw-config est aussi relu à chaque
  exécution (le 2e montage gardait le folder_id du 1er chargement).

Inclus également le travail en cours de l'arbre : Library (colonnes Last
visited/Source, ordre d'en-tête, favoris à icônes Workspace), Meeting
Notes (bloc, CSS, routes, docs), coloration de code hljs, badges
favori/publié dans l'arbre local-workspace, docs (DATA_MODEL,
architectures) et tests associés.
2026-10-09 16:58:04 -04:00

5.7 KiB

V7.1.0 — Calendar sync + Meeting Notes · V7.1.1 — Bloc façon Notion

Statut : ✅ Livré en v7.1.0 (2026-09-28) — services/calendar_sync.py, services/meetings.py, routers/meetings.py, migration 27, 15 tests verts · Refonte Notion livrée en v7.1.1 (migration 38, static/js/meeting_block.js, static/css/meeting.css, 5 tests verts tests/test_v711_meeting_notion.py) · Roadmap : ROADMAP.md § v7.1.0 Notes d'implémentation : pas de lib CalDAV/Google (REST + XML bruts) ; refresh OAuth hors scope (relink explicite sur 401) ; conflit = LWW auto + notif (pas de picker « garder ma version ») ; free/busy au jour (pas de créneaux horaires). Référence Notion : Notion Calendar (Google sync) + AI Meeting Notes → trigger Custom Agents (07/2026). Spec détaillée : docs/architecture-meeting-notion-flowdeck.md (§3A parcours visuel des 6 états, §3B maquettes ASCII, §6.5 machine à états, §12.7 étapes Thinking, §12.8 complétude + titre). Existant : vues jour/semaine/mois, moteur recurrence.py, reminders.py (scan 60s, dédup), timezones, notify_assignment(), template Meeting Notes.


1. Sync externe bidirectionnelle

CREATE TABLE calendar_links (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
  provider TEXT NOT NULL,              -- google|caldav
  tokens_enc TEXT NOT NULL DEFAULT '', -- Fernet (refresh + access)
  calendar_id TEXT NOT NULL DEFAULT 'primary',
  sync_token TEXT DEFAULT '',
  last_sync TIMESTAMP, UNIQUE(user_id, provider, calendar_id)
);
ALTER TABLE collection_pages ADD COLUMN external_event_id TEXT;
CREATE TABLE meeting_transcripts (
  id INTEGER PRIMARY KEY AUTOINCREMENT, page_id INTEGER NOT NULL REFERENCES pages(id) ON DELETE CASCADE,
  audio_path TEXT NOT NULL DEFAULT '', transcript TEXT NOT NULL DEFAULT '',
  summary TEXT NOT NULL DEFAULT '', language TEXT DEFAULT 'fr',
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
  • OAuth Google (app/auth/providers/google_calendar.py, scope calendar.events) + CalDAV générique (URL/user/pass chiffrés).
  • Pull 15 min : event externe → page (titre + date + __external_id__) ; push : occurrence FlowDeck → event (si sync_enabled par collection).
  • Conflit (modifié des 2 côtés depuis last_sync) → LWW + notif calendar.conflict + lien « garder ma version / prendre externe ».
  • GET /db/{id}/calendar/freebusy?attendees=&from=&to= : créneaux libres (jour ouvré 9h-18h, fuseau user).

2. Meeting Notes v2

Upload .mp3/.wav/.m4a (100 MB) sur page Meeting → meeting_transcripts → transcription (Whisper local si binaire présent, sinon API via llm_config, timeout 10 min, job fond + polling) → résumé ai_writing.py (Décisions, Next steps [[person]], Action items) inséré en blocs + Agenda/Notes existants complétés. Émet meeting.summarized → automations v7.0 (update tracker, post Slack, file tickets).

3. Tests (tests/test_v71_calendar_meetings.py, ~15)

OAuth mock (tokens chiffrés roundtrip), pull/push, conflit LWW + notif, freebusy, transcription mock → résumé blocs, trigger émis, quota 100 MB refusé.

4. V7.1.1 — Refonte du bloc façon Notion (§3A/3B de architecture-meeting-notion-flowdeck.md)

  • Bloc à états (static/js/meeting_block.js surcharge window.FDMeeting, styles static/css/meeting.css) : idle → recording → paused → processing → done/failed, révélation progressive Notes → Notes+Transcript → Summary+Notes+Transcript (Summary en tête, actif par défaut après Stop), bouton Start à menu (source/mode, langue), onde en direct + chrono, pastille Thinking, barre Share this summary (Copy link / Email / Slack tracés), pied instructions + attestation de consentement puis bandeau. Le titre généré par l'IA ne touche jamais le titre de la page hôte. Anciens blocs migrés à la volée.
  • Capture Web 2 modes : A micro (getUserMedia), B onglet + micro (getDisplayMedia + micro, canaux séparés, alerte « je n'entends que vous » après 20 s de canal distant muet) ; STT navigateur quand dispo, transcript manuel + import audio en secours, tampon IndexedDB avec reprise après rechargement (recording → paused, processing → suivi repris).
  • Backend (migration 38) : colonnes état/mode/instructions photo/consent/segments/steps/résumé structuré/titre/complétude + tables append-only meeting_consent_log, meeting_distributions + unicité une occurrence = une note. Endpoints : POST /transcripts (création idempotente), /consents, /sessions, /pause, /resume, /stop (analyse inline), /segments, /transcript (groupé par source), /processing-steps, /processing:retry, /generated-title (PATCH), /distributions, /notes (PATCH), GET /meetings/history + /meetings/instructions.
  • Pipeline IA (services/meetings.py) : étapes Thinking persistées (lecture → analyse → résolution des références → compréhension → complétude → résumé → titre → citations), résumé contraint Overview/Décisions/Actions/Questions avec citations ¹ [seg N] obligatoires et états vides explicites, classification complete/brief/incomplete/fragmentary (jamais de refus : résumé dégradé + titre-diagnostic Brief or Incomplete Meeting Recording), audit via meeting.summarized inchangé.
  • Hub (base.html) : onglets Upcoming / Today / Past, recherche plein texte, CTA contextuels (Join & transcribe / Prepare / View summary).
  • Tests : tests/test_v711_meeting_notion.py (5) — porte de consentement, machine à états complète avec résumé dégradé + titre-diagnostic, résumé structuré complet, unicité par occurrence, instructions + historique.