Files
flowdeck/ROADMAP.md
T
bruno 6dfd6d718e
FlowDeck CI / docker (push) Successful in 1m20s
FlowDeck CI / lint (push) Successful in 1m27s
FlowDeck CI / test (push) Successful in 10m59s
feat: v6.5.1 — 7 tests webhooks_v2 dé-skipés (0 skip, 749 verts) + roadmap rattrapée (sync.py Bearer coché)
2026-09-24 09:10:08 -04:00

936 lines
73 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.
# Roadmap FlowDeck — Notion Clone
> **Vision**: Copie conforme de l'UI/UX Notion, intégrée à Gitea/GitHub.
> **MVP en production** : v4.0.0 — authentification locale/OAuth, pages blocs, sidebar, library, share/publish, intégrations.
> **Branche principale** : `main` (production) · `develop` (intégration) · `feat/*` (features).
## 🏗️ Stratégie de branches
```
main ──●────────────●────── production (tags vX.Y.Z)
\ /
develop ●──●──●──●────── intégration continue
\ \ \
feat/xxx ● ● ●──── feature branches
```
- **`main`** : code en production, déploiement Docker automatique
- **`develop`** : branche d'intégration, merges des `feat/*`
- **`feat/<nom>`** : une branche par feature/fix, PR → code review → merge dans `develop`
- **Tags** `vX.Y.Z` sur `main` pour chaque release
**Workflow** :
```bash
git checkout -b feat/mon-truc develop
# ... coder, commit, push ...
# Créer une PR feat/mon-truc → develop sur Gitea
# Après review + CI verte → merge
# Pour release: PR develop → main + tag vX.Y.Z
```
**Règles** :
- Ne jamais commiter directement sur `main`
- Une branche = une feature / un fix
- Tests passent avant merge (CI Gitea Actions)
- Commit + push après chaque modification significative
---
## Completed
### v0.2.0 ✅ — Base
Dashboard + Board Kanban + Gitea sync + Notes + Docker
### v0.3.0 ✅ — Auth
OAuth2 Gitea, token par utilisateur, CSRF, rate limiting, webhooks
### v0.4.0 ✅ — UI Notion
Layout 3 zones (sidebar 240px + topbar 44px), dark mode, CSS 31KB, Alpine.js
### v0.5.0 ✅ — Kanban
Colonnes, groupes, cartes enrichies, drag&drop, modale détail, checklists, commentaires
### v0.6.0 ✅ — Table View
6 colonnes triables, groupes rétractables
### v0.7.0 ✅ — Filtres & Tri
Filtres cumulables AND, status dropdown, sort multi-critères
### v0.8.0 ✅ — Vues Spéciales
Status Overview (donut SVG), Team Load (barres), Detailed Board
### v0.9.0 ✅ — Backend
Propriétés custom, AI keywords, sync API, 12 tables DB
### v1.0.0 ✅ — Production
- [x] FastAPI lifespan (deprecation fix)
- [x] 28 tests — 5 vues, filtres, tris, APIs, CSRF, CORS, DB
- [x] CI/CD Gitea Actions (`.gitea/workflows/ci.yml`)
- [x] CHANGELOG.md, ARCHITECTURE.md à jour
- [x] Version 1.0.0 partout (main.py, api.py, health)
- [x] Tag git v1.0.0
### v1.1.0 ✅ — Pages & Sidebar
- [x] Pages Markdown avec arbre imbriqué (parent_id, sort_order)
- [x] Block editor mode (content_format='blocks', JSON storage)
- [x] Soft delete → Trash → Restore → Permanent delete
- [x] Drag & drop réorganisation pages
- [x] Sidebar Notion-style avec sections Recents/Private/Shared
- [x] "+ New page" footer button
- [x] Workspace header avec avatar + chevron toggle
### v1.2.0 ✅ — Éditeur Notion
- [x] Slash menu (/) 15 commandes fonctionnelles
- [x] Navigation clavier (flèches, Enter, Escape)
- [x] Placeholders Notion-style (Heading 1/2/3/4, Toggle, Quote, Code)
- [x] Suppression outline blanc sur les blocs
- [x] Heading 4 support
- [x] Breadcrumbs contextuels (workspace > projet > page)
### v1.3.0 ✅ — Database Concept
- [x] Tables `collections`, `collection_pages`, `collection_views`
- [x] Rétrocompatibilité Gitea via `GiteaBoardCompat`
- [x] Router `/db` — API CRUD collections + pages (12 endpoints)
- [x] 34/34 tests passent
### v1.4.0 ✅ — Propriétés Avancées
- [x] Table `collection_properties` — 21 types Notion
- [x] Service `property_types.py` — validation, formatage, auto-values
- [x] 38/38 tests passent
### v1.5.0 ✅ — Relations & Rollups
- [x] Type `relation` — lien bidirectionnel entre collections
- [x] Type `rollup` — aggregation via relation (COUNT, SUM, AVG, etc.)
- [x] Type `formula` — moteur d'expressions (19 fonctions)
- [x] Formula engine + Rollup engine
- [x] 43/43 tests passent
### v1.6.0 ✅ — Vues Manquantes
- [x] Calendar, Gallery, List, Timeline, Table views
- [x] View tabs navigation
- [x] 49/49 tests passent
### v1.7.0 ✅ — Vues Améliorées
### v1.8.0 ✅ — Sub-items & Dépendances
- [x] View config API, Save view as
- [x] Sub-items (parent_id auto-référence)
- [x] Status aggregate, Dependencies (blocking constraint)
- [x] 56/56 tests passent
### v1.9.0 ✅ — My Tasks & Dashboard Unifié
- [x] Vue `/my-tasks` — agrège pages assignées à l'utilisateur
- [x] Groupement par collection, filtres globaux
- [x] 60/60 tests passent
### v2.0.0 ✅ — Éditeur Complet & Multi-Utilisateurs
- [x] Workspaces, Comments, Page History, Favorites
- [x] Database Templates, Page Templates, CSV Import/Export
- [x] Public Sharing
- [x] 67/67 tests passent
### v2.1.0 ✅ — Workspace Local & Arborescence
### v2.2.0 ✅ — Tags & Recherche Avancée
### v2.3.0 ✅ — Multi-Vues Workspace & Preview Panel
### v2.4.0 ✅ — Tags Personnalisés par Utilisateur
### v2.5.0 ✅ — Administration Avancée
### v2.6.0 ✅ — My Account & Auth Locale
### v2.7.0 ✅ — Intégration Gitea Phase 1
### v2.7.1 ✅ — Private Pages Gitea
### v2.8.0 ✅ — Gitea Phase 2
- [x] Créer fichiers dans le repo (New file UI + API PUT)
- [x] Upload fichiers binaires (POST /upload, drag-drop)
- [x] Syntax highlighting via Prism.js CDN
- [x] Labels Gitea → tags FlowDeck (POST /sync-labels, 16 labels importés)
- [x] Historique commits par fichier (GET /commits?path=)
### v2.9.0 ✅ — GitHub OAuth Provider
- [x] GitHubProvider dans `app/auth/providers.py`
- [x] GitHubAdapter dans `app/services/github_adapter.py`
- [x] Config GitHub dans `.env.example`
### v3.0.0 ✅ — UX Professionnelle
- [x] Multi-sélection (checkboxes + Shift/Ctrl-click dans le sidebar)
- [x] Raccourcis clavier (Ctrl+N, F2, Delete, Escape, Ctrl+S)
- [x] Inline rename (double-clic → champ inline)
- [x] Loading skeletons (animations pulse dans workspaces + file viewer)
- [x] Grid/card view (mode grille/vignettes)
- [x] Table sorting (tri par colonnes)
- [x] Sélecteur de vue: Tree, List, Details, Title, Content (5 modes)
- [x] Preview Panel latéral (contenu, métadonnées, tags, actions)
- [x] Tri par date (Oldest/Newest)
### v4.0.0 ✅ — Accounts, Integrations & Sharing (MVP)
- [x] `auth_method` sur users (local/gitea/github)
- [x] Badge OAuth dans sidebar
- [x] Settings page avec Connect/Disconnect Gitea + GitHub
- [x] Sidebar rules (Private = distant seulement, Trash local-only)
- [x] Library page avec tabs + filtres source (All/Local/Gitea/GitHub)
- [x] Modale Share (2 tabs: Share + Publish)
- [x] Page publique `/p/<slug>` (no auth, rendu HTML)
- [x] Table `page_shares`, colonnes `is_published`, `is_shared`, `share_mode`
- [x] Table `recents` — tracking automatique
- [x] Édition unifiée local/distant (même UI, bouton Commit pour Gitea)
- [x] 133 tests passent
---
## 🔜 Prochaines versions
### v4.0.1 — Onboarding & Polish ✅ (2026-07-18)
> **Objectif** : rendre l'app utilisable dès la première visite, sans friction. **COMPLETED**.
- [x] **Landing page `/` pour visiteurs non-auth** — template `landing.html`, design Notion dark
- [x] **Page `/auth/register` GET** — formulaire d'inscription dédié (tab register actif)
- [x] **Redirection `/` intelligente** — non-auth → landing, auth sans Gitea → local workspace, auth+Gitea → dashboard
- [x] **Empty states** — déjà présents dans local workspace (« Welcome to your Workspace »)
- [x] **Page 404 stylisée** — thème Notion sombre, JSON pour `/api/*`, HTML pour le reste
- [x] **Unifier les routes API** — `/api/pages` GET/POST → 307 redirect vers `/board/api/pages`
- [x] **Page Settings** — lien déjà présent dans le menu utilisateur (Settings → `/accounts/settings`)
### v4.0.2 — Qualité & Robustesse ✅ (2026-07-18)
> **Objectif** : zéro crash, zéro regression, DX impeccable.
- [x] **Gestion d'erreurs** — try/catch dans create_page, message user-friendly (500 JSON)
- [x] **Validation inputs** — login/register déjà validés, titre vide → "Untitled"
- [x] **Rate limiting** — 100 req/min/IP déjà en place, testé OK
- [x] **Session expiry UX** — redirect avec ?expired=1, bannière sur la page login
- [x] **CSRF token refresh** — auto-refresh sur 403, endpoint `/api/csrf-token`
- [x] **Mobile responsive** — sidebar slide-in, modales centrées, landing adaptative
- [x] **Tests de non-régression** — +10 tests (landing, register, 404, validation, duplicate, expiry)
- [x] **143 tests passent**
---
### v4.1.0 — Database: Data Sources & Linked Databases ✅ (2026-07-21)
> **Objectif** : Collections multi-sources, databases liées (linked). **COMPLETED**.
> **Doc** : [`NOTION_DATABASE_TASKS_GUIDE.md` Phase 1](docs/NOTION_DATABASE_TASKS_GUIDE.md#14-plan-implémentation)
- [x] **Table `collection_data_sources`** — multi-sources par collection (FK `collection_id` + `source_collection_id`, flag `is_linked`)
- [x] **API sources** — `GET /db/{id}/sources/api` (liste), `POST /db/{id}/sources/api` (ajouter), `DELETE /db/{id}/sources/{sid}/api` (retirer)
- [x] **API linked DB** — `POST /db/{id}/linked/api` (crée une collection liée : copie vues + propriétés + data source `is_linked=1`)
- [x] **Full-page vs Inline** — `POST /db/{id}/toggle-inline/api` (toggle), `POST /db/inline/api` (créer inline avec `parent_page_id`)
- [x] **Règle permissions** — linked DB hérite du `workspace_id` de la source → mêmes ACLs
- [x] **Migration** — ajout colonnes `workspace_id`, `created_by` sur `collections`
- [x] **153 tests passent** (+10 nouveaux)
### v4.2.0 — Database: Templates & Dashboards ✅ (2026-07-21)
> **Objectif** : Templates réutilisables + dashboards combinant vues/widgets. **COMPLETED**.
- [x] **Table `page_templates` enrichie** — colonnes `description`, `is_recurring`, `recurrence_rule` (daily/weekly/monthly/yearly)
- [x] **Table `collection_dashboards`** — layout widgets (grille configurable : `columns`, `widgets` avec `view_id`, `x`, `y`, `width`, `height`)
- [x] **API templates CRUD complet** — `PUT /workspace/collections/{id}/templates/page/{tid}` (update), `DELETE` (delete)
- [x] **API dashboards CRUD** — `GET/POST/PUT/DELETE /workspace/collections/{id}/dashboards`
- [x] **159 tests passent** (+6 nouveaux)
### v4.3.0 — Database Views complètes (10 types) ✅ (2026-07-21)
> **Objectif** : Tous les types de vues Notion avec layouts. **COMPLETED**.
- [x] **Chart** — barres, courbes, camemberts, donuts, scatter (Chart.js CDN)
- [x] **Form** — formulaire HTML auto-généré depuis les propriétés → POST création page
- [x] **Map** — Leaflet/OSM pour propriété Place (lat,lng parsing)
- [x] **Feed** — vue chronologique type fil d'actualité (newest first)
- [x] **Timeline/Gantt** — barres horizontales avec group_by et plages de dates
- [x] **View tabs** — 11 onglets de navigation (table, board, calendar, gallery, list, timeline, gantt, chart, form, map, feed)
- [x] **Layout options** — card_size, chart_type, group_by, place_property via config_json
- [x] **166 tests passent** (+7 nouveaux)
### v4.4.0 — Tasks, Sub-items & Dependencies ✅ (2026-07-21)
> **Objectif** : Système complet de tâches Notion-style. **COMPLETED**.
- [x] **Flag `is_task`** sur collections — PUT /db/{id}/toggle-task/api (Turn into Tasks)
- [x] **Table `page_dependencies`** — bloque/bloqué par/related avec auto_shift
- [x] **API dependencies** — GET/POST/DELETE /db/{c}/pages/{p}/dependencies/api
- [x] **Auto-shift dates** — POST /db/{c}/pages/{p}/auto-shift/api (skip_weekends option)
- [x] **171 tests passent** (+5 nouveaux)
### v4.5.0 — Sprints & My Tasks ✅ (2026-07-21)
> **Objectif** : Sprints agiles + vue My Tasks cross-databases. **COMPLETED**.
- [x] **Tables `sprints`, `sprint_pages`** — sprints agiles avec velocity_points
- [x] **API sprints** — CRUD + assignation/retrait pages
- [x] **Burndown chart** — GET /workspace/collections/{id}/sprints/burndown/{sid} (total/completed/remaining/ideal)
- [x] **My Tasks** — agrégation cross-databases avec filtre (all/today/overdue/upcoming)
- [x] **175 tests passent** (+4 nouveaux)
### v4.6.0 — Content Blocks enrichis ✅ (2026-09-02)
> **Objectif** : parité d'édition avec Notion.
- [x] **Callout blocks** — boîtes colorées avec sélecteur d'emoji/icône
- [x] **Table of contents** — auto-généré depuis les headings avec ancres
- [x] **Math equations** — LaTeX / KaTeX (self-hosted) block
- [x] **Toggle lists** — contenu expandable/collapsible avec enfants
- [x] **Multi-colonnes** — layout flexible (2, 3 colonnes) + bouton ajouter/retirer colonne
Détails livrés :
- 5 types de blocs enrichis dans le slash menu (callout, table_of_contents, math, columns, toggle)
- Rendu des nouveaux blocs dans les pages publiques (`/p/<slug>`) — TOC ancré, KaTeX, colonnes, toggle `<details>`
- Intégration KaTeX 0.16.11 self-hosté (`/static/js/katex.min.js`, `/static/css/katex.min.css`, `/static/fonts/`)
- Persistance `children` (colonnes/toggle) via le format de blocs JSON
- Export Markdown étendu aux nouveaux blocs
- 183 tests au total (dont 5 nouveaux tests v4.6.0)
### v4.7.0 — Export ✅ (2026-09-03)
> **Objectif** : exporter une page (ou un site) depuis l'éditeur.
- [x] **Export Markdown** — avec images et sous-pages (récursif)
- [x] **Export PDF** — via xhtml2pdf (fidèle, sans CDN/lib système)
- [x] **Export HTML** — site statique standalone (document autonome + site .zip)
Détails livrés :
- Service serveur `app/services/export.py` : conversion blocs → Markdown / HTML / PDF
- 4 formats exposés : `/api/export/markdown|html|pdf|site/{page_id}`
- Sous-pages incluses récursivement (Markdown et site)
- UI : menu « More › Export » dans l'éditeur (Markdown, HTML, PDF, Site .zip)
- PDF généré via `xhtml2pdf` (pip) — aucun lib système requise
- 6 nouveaux tests (Markdown, sous-pages, HTML, PDF, Site, 404)
### v4.7.1 — Fix UI Export / Share / More ✅ (2026-09-03)
- [x] **Share / More / Activity / Move-to popovers** — rendus sous le viewport (absolute + parent scrollable) → repositionnés fixed sous la topbar
- [x] **Sous-menu Export** — l'itém Export fermait le menu More ; devient un toggle, formats accessibles
- [x] **Cache CSS** — query string app.css bumpé v4.7.1
- [x] Vérifié Playwright headless + 190/190 tests
### v4.7.2 — Fix Export : contenu absent ✅ (2026-09-03)
- [x] **Export MD/HTML/PDF des documents** — le service ne lisait que les pages `blocks` ; les pages `file` (upload `.md`/code, contenu sur disque) et `markdown` sortaient avec le seul titre. Résolution de la vraie source pour les 3 formats + rendu HTML/PDF correct des headings/listes.
- [x] 5 nouveaux tests → 195/195 ; vérifié sur les vraies données
### v4.7.3 — Fix export PDF/HTML : tableaux + émojis ✅ (2026-09-03)
- [x] **Tableaux** — parseur GFM (bloc `table`) + rendu `<table>` HTML (thead/tbody, alignements, bordures `.ftable`) dans les exports HTML/PDF ; roundtrip markdown pipe valide.
- [x] **Émojis PDF** — passage du moteur à **WeasyPrint** (émojis couleur via Pango + fonts-noto-color-emoji dans l'image) ; repli automatique xhtml2pdf si libs natives absentes (dev Windows).
- [x] Dockerfile : libs pango/harfbuzz/gdk-pixbuf + polices ; requirements : + weasyprint==69.0
- [x] 4 nouveaux tests → 199/199 ; PDF README.md vérifié : tableau structuré + émojis colorés
### v4.8.0 — Bloc Tableau simple ✅ (2026-09-03)
- [x] **Bloc `table` éditable dans l'éditeur** — insertion via `/table` (commandes slash, section BASIC), tableau 3×3 avec en-tête, cellules éditables au clic, auto-sauvegarde.
- [x] **Lignes** — bouton `+ Row` sous le tableau (clic = ajouter) ; clic droit sur une cellule de corps → Insérer dessus/dessous, Dupliquer, Effacer, Supprimer la ligne, Supprimer le tableau.
- [x] **Colonnes** — poignée (⋮⋮) au-dessus de chaque colonne → Insérer à gauche/droite, Dupliquer, Effacer le contenu, Supprimer la colonne, Supprimer le tableau.
- [x] **Import Markdown (GFM)** — `md2b` transforme les tableaux pipe en bloc `table` au lieu de les aplatir en paragraphes ; roundtrip Markdown (JS + export) et sortie MD/HTML/PDF correcte d'un bloc `table`.
### v4.8.1 — Fix bloc Tableau ✅ (2026-09-03)
- [x] **« + Row » (bas) corrigé** — l'insertion de ligne utilisait `splice(index, ligne)` (mauvaise signature) → aucune ligne ajoutée ; désormais `splice(index, 0, ligne)`.
- [x] **Bouton « + » à droite** — ajoute une colonne à droite (équivalent « Add column »).
- [x] **Redimensionnement des colonnes** — curseur `col-resize` au survol des bordures d'en-tête, drag = largeur ; persisté via `colsW`.
### v4.9.0 — Collaboration ✅ (2026-09-04)
> **Objectif** : commentaires inline, mentions @, notifications in-app + email. **COMPLETED**.
> **Doc** : [`docs/Guide_Complet_Notion_sharing_collaborartion.md`](docs/Guide_Complet_Notion_sharing_collaborartion.md)
- [x] **Inline comments** — commentaires sur sélection de texte dans l'éditeur (bouton flottant 💬) ; table `comments` étendue (`target_type`/`target_id`/`anchor_block_id`/`anchor_start`/`anchor_end`) et migrée (FK générique, idempotente) ; panneau de commentaires + résolution/suppression + compteur topbar
- [x] **@mentions** — autocomplétion `@` (recherche utilisateurs temps réel), insertion `@login` dans les blocs et commentaires, table `notifications` ; endpoint `POST /api/pages/{id}/mentions` (notif des mentions de contenu)
- [x] **Email notifications** — service SMTP (`app/services/mailer.py` + templates HTML) ; préférences email par utilisateur (comments/mentions) ; config `.env` (`SMTP_*`, `APP_BASE_URL`) ; repli no-op sans SMTP
- [x] **Centre de notifications in-app** — cloche topbar (badge non-lus, polling 30s), panneau déroulant, mark as read / mark all read
- [x] **Settings** — toggles réels dans Settings → Notifications (Commentaires / Mentions)
- [x] **208 tests passent** (+9 v4.9.0)
### v4.11.0 — Agent IA : clés API par utilisateur & commandes slash ✅
> **Livré (2026-09-05)** : credentials par utilisateur (table `user_llm_keys`, plusieurs providers),
> chargement dynamique des modèles depuis le fournisseur (`POST /keys/{p}/models`),
> commandes slash dans le chat (`/provider`, `/model`, `/keys`, … via `PATCH` conversation),
> `/run` utilise la clé de l'utilisateur. 239 tests verts.
### v4.10.1 — Agent IA : config LLM dans l'UI ✅
> **Livré (2026-09-05)** : sélecteur provider/modèle dans le panneau agent (persisté par
> conversation), config runtime DB-backed (`llm_config`), écran admin *Agent & IA* avec
> test de connexion (`LLMClient.ping()`, sans fallback mock). 232 tests verts.
### v4.10.0 — FlowDeck Agent (Agent IA natif) ✅
> **Livré (2026-09-05)** : agent conversationnel complet — boucle ReAct, streaming SSE,
> 18 outils avec snapshots rollback, ACL par rôle, contexte automatique, custom agents,
> déclencheurs planifiés + skills, multi-LLM (offline mock + 8 providers). 18 tests dédiés.
> Voir [`docs/Flowdeck_Agent_integration.md`](docs/Flowdeck_Agent_integration.md).
**Objectif :** Un agent IA intégré à FlowDeck, capable de planifier, rechercher et **agir** directement sur les workspaces — collections, pages, propriétés, vues, issues Gitea. Inspiré de Notion Agent (2026).
**Documentation :**
[`docs/Flowdeck_Agent_integration.md`](docs/Flowdeck_Agent_integration.md) (702 lignes) ·
[`docs/Guide_Complet_Notion_Agent_2026.md`](docs/Guide_Complet_Notion_Agent_2026.md) (464 lignes)
#### Concept
Là où un assistant IA classique répond (`prompt → réponse`), FlowDeck Agent **réalise** :
`objectif → planification → collecte contexte → actions API → résultat`
| Notion AI | FlowDeck Agent |
|-----------|----------------|
| Répond, résume, réécrit | Planifie, recherche, **agit** |
| Bloc de texte | Crée collections, pages, propriétés, vues |
| Sans état | Historique conversation + snapshots rollback |
| Aucune action DB | Modifie le workspace via API FastAPI existantes |
#### Fonctionnalités clés
- [x] **Interface conversationnelle** — panneau agent dans le sidebar (🤖 Agents) + popup chat
- [x] **Boucle ReAct** — Raisonnement → Action → Observation → itération (max 12 tours)
- [x] **Streaming SSE** — affichage temps réel du raisonnement et des actions
- [x] **10+ outils actionnables** — search, read/write collections/pages, views, properties, Gitea sync
- [x] **Permissions** — même ACL que l'utilisateur (`PermissionManager`), audit log + rollback
- [x] **Contexte automatique** — workspace actif, pages mentionnées, fichiers uploadés
- [x] **Custom agents** — instructions, modèle, outils, scope par agent
- [x] **Déclencheurs** — planifiés, webhooks, événements workspace
- [x] **Skills réutilisables** — templates d'instructions + outils (ex: "Rédiger rapport hebdo")
- [x] **Multi-LLM** — GPT, Claude, Gemini, modèles locaux (Ollama), deepseek, qwencloud, Nvidia, openrouter
#### Architecture (nouveaux composants)
```
app/
├── routers/agent.py — /api/agent/* (run, conversations, tools)
├── services/agent_engine.py — Orchestrateur ReAct + boucle tool calls
├── services/llm_client.py — Abstraction multi-fournisseur LLM
├── services/tool_registry.py — 10+ outils wrappant les API existantes
├── services/context_builder.py — Collecte contexte workspace
└── templates/
├── agent_panel.html — Panneau conversation Alpine.js
└── agent_message.html — Fragment message (streaming)
```
#### Nouvelles tables
| Table | Rôle |
|-------|------|
| `agents` | Config agent (nom, instructions, modèle, tools scope) |
| `agent_conversations` | Sessions (titre, agent, workspace, statut) |
| `agent_messages` | Messages (role, content, tool_calls, artifacts) |
| `agent_actions` | Journal d'audit (tool, args, result, undo_snapshot) |
| `agent_skills` | Skills réutilisables (nom, prompt template, outils) |
| `agent_triggers` | Déclencheurs (cron, webhook, event, agent cible) |
#### Cas d'usage FlowDeck
- **Créer un CRM complet** — agent crée collection, propriétés, vues, exemples
- **Résumer les notes de réunion** — lit les pages, synthétise, crée une page résumé
- **Générer des OKR** — crée la database OKR avec relations et vues
- **Analyser un repo Gitea** — lit les issues, crée un dashboard de suivi
- **Rédiger un rapport hebdomadaire** — agrège les pages modifiées, génère le rapport
#### Plan de migration (5 phases)
1. **Phase 1 — Core Agent** : AgentEngine + LLMClient + 3 outils (search, read, create) + SSE streaming
2. **Phase 2 — UI** : agent_panel.html, historique conversations, sélecteur de modèle ✅ (v4.10.1)
3. **Phase 3 — Outils avancés** : 7 outils supplémentaires (views, properties, Gitea, uploads)
4. **Phase 4 — Autonomie** : custom agents, skills, déclencheurs planifiés
5. **Phase 5 — Plateforme** : API publique agent → intégrations tierces, marketplace skills
#### Limitations (v1)
- Pas de modification concurrente (lock optimiste DB)
- Pas d'appels API externes non-FlowDeck (sandbox strict)
- Budget tokens max par conversation (500k tokens)
- Timeout 5 minutes par run
### v5.0.0 — Command Palette & Recherche ✅ (2026-09-06)
> **Objectif** : `Ctrl+K` / `Ctrl+P` palette de commandes universelle + recherche full-text. **COMPLETED**.
- [x] **Command palette** — Ctrl+K / Ctrl+P recherche universelle (modale, fuzzy, navigation clavier)
- [x] **Quick actions** — navigation, création, commandes
- [x] **Recherche full-text** — SQLite FTS5 sur pages + propriétés (prérequis technique de la palette)
### v5.1.0 — Automations ✅ (2026-09-07)
> **Objectif** : moteur de règles if-this-then-that + boutons cliquables. **COMPLETED**.
- [x] **Database automations** — moteur de règles if-this-then-that (trigger + condition + action)
- Déclencheurs : événement (`page.created/updated/deleted/moved`, `collection.*`) / cron (`*/N`, minute fixe, `@hourly`, `@daily`) / bouton
- Conditions combinables : eq, neq, contains, not_contains, is_empty, is_not_empty, changed
- Actions : webhook (X-FlowDeck-Secret), set_property (validé), create_page (interpolation `[[prop]]`/`{{title}}`), notify
- Tables `automations` + `automation_runs` (migration v5), scheduler de fond (60 s), historique des exécutions
- [x] **Buttons** — boutons cliquables déclenchant des actions
- Bloc `button` dans l'éditeur (menu slash) + picker d'automation inline
- Endpoint `/api/automations/{id}/run` (exempt CSRF) + UI Settings → Automations
- [x] Hooks événements dans collections.py / board.py / workspace.py (apply_page_template)
- [x] **11 tests** `tests/test_automations.py` ; suite complète 289 verte
### v5.2.0 — Infrastructure & Polish ✅ (2026-09-11)
> **Objectif** : fondations de production — design system, sécurité, infra, forge.
> **COMPLETED**.
**Design system**
- [x] **Design tokens** — `static/css/design-tokens.css` (couleurs, espacements, typo, ombres, z-index) chargé après `app.css`
- [x] **Composants réutilisables** — `static/css/components.css` (btn/input/modal/dropdown/toast/card/badge/empty/table)
**Sécurité & Utilisateur**
- [x] **API Tokens** — générer/lister/révoquer dans Settings → API tokens (`app/routers/security.py`, table `api_tokens`, bearer `/api/v1`)
- [x] **Sessions actives** — liste + révocation (`user_sessions`, `SessionManager.list_sessions/revoke_session`)
- [x] **Onboarding wizard** — `/welcome` (workspace → forge → premier projet) + `welcome.html`
**Infrastructure**
- [x] **🥇 Migrations versionnées** — table `schema_version` + runner `app/migrations.py` (baseline v1, indexes v2, FTS5 v3)
- [x] **Backup automatique** — snapshot SQLite quotidien (`app/services/backup.py`, scheduler + API admin, rétention configurable)
- [x] **Index manquants** — `users.email`, `user_oauth_tokens(user_id, provider)`, `collections/pages(workspace_id)`, `pages(deleted_at)`
- [x] **Linting** — ruff (Python, `pyproject.toml`) + eslint flat (`eslint.config.mjs`) ; `ruff check` et `eslint` sans erreur
- [x] **Tests parallèles** — pytest-xdist (`pytest -n auto` en local et en CI ; DB + dossier backup isolés par test)
- [x] **Build Docker multi-stage** — builder + runtime (libs WeasyPrint), image allégée
**Forge integration**
- [x] **DB: table `projects`** — type (builtin/gitea/github), forge_id, clone_url, default_branch, language
- [x] **Cron: sync périodique des projets** — `project_sync_scheduler` (défaut : chaque heure)
- [x] **GitHubAdapter** complet — API GitHub v3 → interface `ForgeAdapter`
**Tests (cibles)**
- [x] **397 tests** — dont backups, projets, adapters forge (mock HTTP), OAuth (mock), multi-user ✅
- [x] Tests d'intégration auth (OAuth mock) — login/callback Gitea + GitHub, mode `link`, rejet d'état invalide
- [x] Tests des adapters forge (mock HTTP) — `GitHubAdapter` via `httpx.MockTransport`
- [x] Tests multi-user (permissions croisées) — `PermissionManager` owner/editor/viewer
**✅ Validation (2026-09-11)** :
- `pytest tests/test_v52_infra.py -v` → **18/18 passed** (tokens, sessions, onboarding, backups, projets, adapters forge, OAuth mock, multi-user)
- `pytest -q` (suite complète) → **397 passed**
- `ruff check app tests` → **All checks passed!** · `eslint static/js` → **0 problème**
- CI Gitea (commit `ba363ea` ; run push #1412 + PR #15 run #1413) → **success** sur `push` **et** `pull_request` : jobs `lint` (≈43 s), `test` (`-n auto`, ≈4 min), `docker` multi-stage (≈1 min)
- Docker : stages `builder` + `runtime` vérifiés par le job CI `docker` (`from app.main import app` OK)
**Complétion du 2026-09-11** :
- Isolation des tests corrigée : `tests/conftest.py` **mute** le singleton `settings` (au lieu de le remplacer), sinon les modules ayant importé `settings` au chargement (ex. `backup.py`) gardaient les valeurs par défaut → backups flaky. DB temporaire unique par test, sûr en xdist.
- Backups réellement testés (snapshot + prune + API admin) : les 2 skips supprimés.
- `init_db()` crée désormais aussi `webhook_subscriptions` (schéma complet sans dépendre du lifespan FastAPI).
- Tests OAuth mock (flux complet) ajoutés dans `tests/test_v52_infra.py`.
- CI : job `lint` (ruff + eslint) + tests parallèles (`-n auto`), déclenché sur toutes les branches.
### v5.3.0 — Database Avancée ✅ (2026-09-06)
> **Objectif** : databases inline + templates prédéfinis + validation propriétés. **COMPLETED**.
- [x] **Inline databases dans n'importe quelle page** — slash command `/database` (groupe DATA) → sélecteur de templates → bloc `embed_type:'collection'` rendu par `FlowDeckDB`
- [x] **Templates de database prédéfinis** — 6 templates seedés (CRM, Project tracker, Task list, Content calendar, Meeting notes, Reading list) + galerie au clic « Database » (Get Started) et `/database` ; `POST /db/api` & `/db/inline/api` acceptent `template` et matérialisent les propriétés
- [x] **Validation des propriétés** (required, unique, min/max) — côté serveur (`validate_property_rule`, 400 + messages) + UI (modale propriété + erreurs de cellule)
### v5.10.0 — Éditeur : interactions de bloc ✅ (2026-09-08)
> **Objectif** : parité de manipulation des blocs avec Notion — les blocs étaient éditables
> mais *statiques* (impossible de les déplacer, dupliquer ou annuler une action). **COMPLETED**.
- [x] **Drag & drop des blocs** — poignée ⋮⋮ au survol de chaque bloc, drag vertical pour réordonner, indicateur de drop (ligne bleue) ; **multi-sélection** (Shift+clic sur poignée → sélection groupée déplacée d'un seul geste, ordre reconstruit depuis le DOM via `data-id`)
- [x] **Menu contextuel de bloc** — clic (ou clic droit) sur ⋮⋮ : Turn into (sous-menu conservant le contenu), Duplicate, Copy link to block (`#fdblk-<id>` avec re-focus), Move to (recherche de pages + API), Delete, couleurs texte/fond appliquées au bloc ou à la sélection
- [x] **Undo / Redo** — `Ctrl+Z` / `Ctrl+Shift+Z`/`Ctrl+Y`, pile de 100 opérations en mémoire du tab, re-focus du bloc restauré, déclencheur sur toutes les mutations (entrée, retour arrière, slash, tableaux, colonnes, toggles…)
- [x] **Duplicate block** — `Ctrl+D`, menu bloc ou slash command « Duplicate » ; copie profonde (enfants columns/toggle) en multi-sélection
- [x] **Slash command étendue** — groupe « Actions » : Turn into, Duplicate, Copy link to block, Delete block
- [x] **En-têtes de tableau** — toggles « Header row » / « First col » dans la barre du tableau (`has_header` défaut actif + nouveau `first_col_header`), persistés + rendu `<th>` en preview et exports Markdown/HTML
- [x] **Intégration realtime** — `syncNow()` poussé après chaque mutation programmatique ; **9 tests** `tests/test_block_interactions.py` ; suite complète 307 verte (+3 PDF pré-existants)
### v5.13.0 — Collaboration temps réel ✅ (2026-09-08)
> **Objectif** : édition collaborative en direct (parité Notion en équipe). **COMPLETED**.
> Chantier n°1 de la parité Notion ; socle pour v5.14.0 (synced blocks).
- [x] **WebSocket gateway** — endpoint `WS /ws/pages/{id}`, auth via cookie session (refus 4401), page introuvable/supprimée → 4404 ; rooms par page en mémoire, chargées depuis la base au premier connect, droppées quand vides
- [x] **Présence** — avatars des utilisateurs connectés (topbar), couleur par utilisateur, join/leave broadcasté ; `welcome` = self + peers
- [x] **Curseurs live** — position curseur/sélection des autres éditeurs (block + offset), calque dédié, label avec nom, mise à jour à l'édition/au scroll
- [x] **Merge de modifications** — diffusion des ops de blocs insert/update/delete/move avec **last-write-wins par bloc** + version de page (stale → sync complet) ; titre synchronisé (debounce) ; persistance debounce ~1 s + flush à la déconnexion du dernier client
- [x] **Fallback polling** — si WS indisponible, rafraîchissement diff toutes les 10 s (adopté seulement sans brouillon local) + reconnexion automatique
- [x] CSP `connect-src` étendu à `ws:` ; **12 tests** `tests/test_realtime.py` ; suite complète 298 verte (+3 PDF pré-existants)
### v5.4.0 — Expérience éditeur ✅ (2026-09-11)
> **Objectif** : parité éditeur Notion — backlinks, duplication, corbeille, versions, import. **COMPLETED**.
- [x] **Backlinks** — `GET /board/api/pages/{id}/backlinks` (scan des liens internes `/pages/<id>`) + popover « Lié depuis… »
- [x] **Duplicates** — `POST /board/api/pages/{id}/duplicate` (copie profonde des blocs) + `POST /db/{id}/duplicate` (vues + propriétés + pages) ; entrées « Duplicate » dans les menus `...`
- [x] **Corbeille globale améliorée** — vue cross-workspace `GET /board/api/trash` + `app/services/trash.py` (purge automatique > 30 jours, scheduler quotidien)
- [x] **Historique de version UI** — snapshots `page_versions` à chaque sauvegarde modifiée, `GET /api/pages/{id}/versions`, `POST .../versions/{vid}/restore`, popover « Version history »
- [x] **Import** — `POST /api/pages/import` (markdown) + `POST /api/pages/import/file` (.md/.txt/.zip d'export Notion) ; import CSV collections `POST /workspace/collections/{id}/import/csv`
- [x] **17 tests** `tests/test_v54.py` ; suite complète **397 verte**
### v5.5.0 — Embeds & Média Riche ✅ (2026-09-12)
> **Objectif** : parité avec les 60+ embeds Notion — contenu tiers rendu dans la page.
> **Source** : analyse Notion clone (delta v4 → v5.5, 2026-09-04). **COMPLETED**.
- [x] **Bloc Embed universel** — `/embed` : YouTube (watch/shorts/youtu.be), Vimeo, Figma, Google Maps, Google Docs/Sheets/Slides, Loom, X/Twitter, CodePen, Miro, Spotify, SoundCloud, Twitch, Pinterest, Office…
- [x] **Bookmark cards** — aperçu riche des URLs (métadonnées OG : titre, image, description, site, favicon)
- [x] **Image lightbox** — clic pour agrandir, navigation clavier (←/→) + plein écran dans l'éditeur **et** les pages publiques
- [x] **Previews inline** — PDF, vidéo, audio rendus directement dans la page
- [x] **Cover & icône de page** — upload image de couverture (fichier ou URL) + emoji/icône custom
Détails livrés :
- Service `app/services/embeds.py` : détection multi-provider + réécriture d'URL, `resolve_embed()`, `inline_kind()`, `embed_html()` ; endpoint `POST /board/api/embed/resolve`
- Service `app/services/og_fetcher.py` : parseur OG robuste (ordre d'attributs libre), favicon, repli sans réseau ; endpoint `POST /board/api/og/metadata`
- Endpoints `POST/DELETE /board/api/pages/{id}/cover` (JSON URL ou upload image) et `POST /board/api/pages/{id}/icon`
- Éditeur : résolution d'embed à la saisie (URL d'origine conservée + `embed_src` mis en cache), lightbox multi-images navigable, préviews vidéo/audio/PDF
- Pages publiques `/p/<slug>` : cover + icône, embed résolu, lightbox clavier
- Export Markdown/HTML/PDF : nouveau bloc `embed`/`bookmark` avec `embed_src` résolu
- **Fix chemins API** : l'éditeur appelait `/api/pages/...` alors que les routes sont `/board/api/pages/...` (cover, icon, versions, backlinks, import, move, OG) — corrigé
- **47 tests** `tests/test_v55.py` ; suite complète **444 verte** ; `ruff check` OK
### v5.6.0 — Import de données (étendu) ✅ (2026-09-13)
> **Objectif** : faire de FlowDeck la cible d'import universelle pour les outils réellement utilisés
> (notes Markdown, bureautique, tableaux, signets, dev), en complétant l'import de base
> (Markdown/CSV/Notion, déjà dans v5.4.0). **COMPLETED**.
> **Sources retenues** : Obsidian, Notion, Logseq/Roam, Apple Notes/Bear/Ulysses, Google Keep,
> OneNote, Word, Google Docs, HTML, PDF, CSV/Excel/Sheets/JSON, Gitea/GitHub, Raindrop, Pocket,
> Readwise, Shaarli, `.ics`, OPML, Standard Notes.
> **Sources exclues** : Confluence, Evernote, Asana, Trello (non utilisées).
#### Phase 0 — Socle d'import unifié (prérequis) ✅
> Toutes les sources partagent le même pipeline ; à livrer avant les imports.
- [x] **Service `app/services/importers/`** — interface commune (`detect()`, `parse()`, `to_pages()`) + résultat normalisé (`ImportResult` : pages, pièces jointes, warnings, stats)
- [x] **Pipeline commun** — upload → parse → normalisation blocs + métadonnées → création (pages + hiérarchie `parent_id`) → rapport d'import
- [x] **Pièces jointes** — extraction (images/fichiers), upload, réécriture des liens dans les blocs
- [x] **Frontmatter YAML → propriétés** (title, tags, dates, champs custom)
- [x] **Assistant UI d'import** — choix de la source, mapping colonnes→types, preview/dry-run, barre de progression
- [x] **Import asynchrone** (job en arrière-plan + polling) pour les gros volumes (vaults, zips)
- [x] **Idempotence / déduplication** + reprise sur erreur partielle
- [x] **Tests** — harnais d'import + fixtures par source
- [x] **Réutilise** : `_md_to_blocks` (`app/services/export.py`), `PROPERTY_TYPES` + `validate_property_rule` (`property_types.py`), `db_templates.materialize_properties`, endpoints upload/cover existants
#### Phase 1 — Notes & Markdown (réutilise `_md_to_blocks`) — faible effort ✅
- [x] **Obsidian** (vault `.zip`/dossier) — frontmatter YAML, `[[wikilinks]]`, `![[embeds]]`, attachments, hiérarchie de dossiers *(prépare v5.11.0)*
- [x] **Notion** (améliorer l'existant) — hiérarchie complète, databases (CSV) → collections, images
- [x] **Logseq / Roam Research** — markdown outliné (puces imbriquées), `((block refs))`, pages journal
- [x] **Apple Notes / Bear / Ulysses** — import export HTML/Markdown
- [x] **Google Keep** — Takeout JSON + HTML
- [x] **OneNote** — export HTML/PDF, best-effort (fidélité limitée)
#### Phase 2 — Données & tableaux (fort ROI databases) — effort faible/moyen ✅
- [x] **CSV/TSV typé** — inférence auto (texte/nombre/date/bool/select/multi_select) + UI mapping + options
- [x] **Excel `.xlsx`** (openpyxl) — multi-feuilles → collections
- [x] **Google Sheets** — export CSV/XLSX
- [x] **JSON générique** — mapping configurable (JSONPath → propriétés)
#### Phase 3 — Documents & bureautique — effort moyen ✅
- [x] **Word `.docx`** (mammoth/pandoc) — titres, listes, tableaux, images
- [x] **Google Docs** — Takeout `.docx`/HTML
- [x] **HTML** (fichiers/dossier, web clipper) — conversion HTML → blocs
- [x] **PDF** — extraction texte + images (fidélité limitée)
#### Phase 4 — Signets, dev & divers ✅
- [x] **Gitea / GitHub issues + labels + milestones** → collection (via API — quasi natif)
- [x] **Raindrop.io** (CSV/HTML) → bookmark cards (réutilise v5.5.0)
- [x] **Pocket** (CSV/HTML)
- [x] **Readwise** (highlights CSV/Markdown)
- [x] **Shaarli** (API/export JSON) → bookmark cards
- [x] **Calendrier `.ics`** (Google/Outlook/Apple)
- [x] **OPML** (flux/outlines)
- [x] **Standard Notes / autres**
#### Phase 5 — Durcissement & finition ✅
> **Objectif** : industrialiser l'import (ré-import, lots, URL, forge, relations, rapports).
- [x] **Import incrémental / re-sync** — modes `skip` / `update` (upsert des pages + lignes par titre) / `duplicate`, via `import_items`
- [x] **Import de dépôt forge** — arborescence de fichiers Gitea/GitHub → pages (hiérarchie de dossiers, fichiers texte, code en blocs)
- [x] **Import par URL / web clipper** — `POST /api/import/url` : fetch (garde SSRF) + OG metadata → page (carte bookmark + contenu)
- [x] **Lot multi-fichiers + file d'attente** — `POST /api/import/run-batch` + file d'attente UI (statut par fichier)
- [x] **Relations Notion** — `POST /api/import/relations/resolve` : colonnes texte référençant une autre collection → propriétés `relation` (ids de pages) ; auto-exécuté après un import Notion
- [x] **Reprise / erreurs partielles + rapport exportable** — import qui continue par page en cas d'erreur (`status: partial`, liste `errors`) ; rapport JSON téléchargeable (`GET /api/import/jobs/{id}/report` + bouton UI)
- [x] **Valeurs de propriétés par id** — correction : les lignes importées stockent les valeurs par id de propriété (rendu correct dans les vues DB)
#### Priorisation
| Ordre | Phase | Effort | Impact |
|---|---|---|---|
| 1 | Phase 0 — Socle unifié | M | 🔴 prérequis |
| 2 | Phase 1 — Notes/Markdown | S–M | 🔴 fort (Obsidian, Notion) |
| 3 | Phase 2 — Données/tableaux | S–M | 🔴 fort (databases) |
| 4 | Phase 4 — Signets/dev/divers | S | 🟠 moyen (Gitea natif, Raindrop) |
| 5 | Phase 3 — Documents | M | 🟠 moyen (docx, HTML) |
### v5.7.0 — Database Avancée (Pt. 2) ✅ (2026-09-13)
> **Objectif** : compléter la parité sur les propriétés, les vues sauvegardées et le Kanban pro. **COMPLETED**.
- [x] **Types propriété** — `person`, `created_time`, `created_by`, `last_edited_time`, `last_edited_by` câblés : auto-valeurs calculées serveur (`apply_auto_properties`) à la création/mise à jour, sélecteur `person` (membres du workspace via `GET /db/{id}/members/api`), rendu chips/avatars et édition dédiée dans l'UI
- [x] **Groupes de propriétés** — colonne `collection_properties.group_name`, header de table avec sections pliables (`db-group-row` + toggle) et `POST /db/{id}/property-groups/api`
- [x] **Vues sauvegardées par utilisateur** — `collection_views.created_by` ; `GET /db/{id}/views/api` filtre par propriétaire (les vues partagées `NULL` restent visibles), `save-as`/`duplicate`/`DELETE` + renommage
- [x] **Swimlanes Kanban** — `sub_group_by` (2e dimension de groupement) : une rangée de colonnes par lane
- [x] **WIP limits** — `wip_limits` par colonne, alerte visuelle au dépassement (`wip-exceeded`)
- [x] **Cartes configurables** — `card_properties`, `card_size` (compact/détaillé), couverture `cover_mode` (`none`/`icon`/`color`/`property`) via `cover_property`
- [x] **Calendar avec drag & drop** — grille mensuelle, `date_property`, navigation mois/Today, drop d'une carte sur un jour = reschedule
- [x] **Gallery avec couvertures** — cartes avec vignette (image de propriété, icône, couleur, ou cover de ligne), tailles small/medium/large
Détails livrés :
- Migration 10 : `collection_properties.group_name`, `collection_views.created_by`/`updated_at`, `collection_pages.cover_url`
- `app/services/property_types.py` : `user_ref()`, `apply_auto_properties()`, validation `person`
- `collections.py` : auto-props create/update/sub-item, PATCH partiel fusionné, membres, groupes, vues per-user (list/save/duplicate/delete), config de vue étendue (`sub_group_by`, `wip_limits`, `card_size`, `cover_mode`, `card_properties`, `date_property`, `property_groups`)
- `dashboard.py` : auto-props à la création de ligne, `views` exposées dans `table-data` et le contexte de page
- `_database_table_scripts.html` réécrit en composant multi-vues (table/board/calendar/gallery/list) + barre de vues, éditeurs de cellules par type, picker personne, popovers de configuration
- **12 tests** `tests/test_v57_db_advanced.py` ; suite complète **503 verte** ; `ruff check` OK
### v5.8.0 — Calendrier & Rappels ✅ (2026-09-13)
> **Objectif** : calendrier complet + notifications proactives. **COMPLETED**.
- [x] **Vues Jour / Semaine / Mois** — sélecteur de mode dans la vue calendar (persisté en config de vue), navigation ‹/› et Today adaptées, vue Jour en agenda horodaté, Semaine en 7 colonnes ; événements servis par `GET /db/{id}/calendar/api` avec expansion serveur des occurrences
- [x] **Récurrence d'événements** — moteur `app/services/recurrence.py` (daily/weekly/monthly, intervalle, count, until, byweekday lundi=0, timezone) ; règle stockée dans `property_values_json.__recurrence__` ; popover événement (double-clic) avec Repeat/Every/Ends ; validation serveur (400)
- [x] **Support timezone** — colonne `users.timezone` + picker dans Settings → Notifications (`GET/POST /api/notifications/timezone`, zones via `GET /db/timezones/api`) ; timezone par événement (`__timezone__`) > règle de récurrence > préférence utilisateur
- [x] **Rappels** — `app/services/reminders.py` : lead minutes/heures/jours avant chaque occurrence, scan toutes les 60 s (`reminder_scheduler`), dédup `reminder_log`, notifie les personnes assignées (repli admin), in-app + email (pref `reminders`) ; stockés dans `property_values_json.__reminder__`, édités dans le popover événement
- [x] **Centre de notifications** — la cloche (v4.9.0) couvre désormais rappels, assignations et commentaires ; nouvelles préférences `reminders` + `assignments` ; notification d'assignation émise par `notify_assignment()` sur `PUT /db/pages/{id}/api` (comparaison avant/après des propriétés `person`)
- [x] **Template Meeting Notes** — propriétés `Agenda` et `Notes` ajoutées au template seed (migration 12, préserve les personnalisations) ; le schéma Attendees/Date/Status/Action items était déjà là
Détails livrés :
- Migrations 11 (`reminder_log`, `users.timezone`) et 12 (template Meeting notes)
- `app/config.py` : `reminders_enabled`, `reminder_scan_interval_seconds`
- `_database_table_scripts.html` : modes jour/semaine/mois, chips d'occurrences (badge ↻ + heure), popover Time/Timezone/Repeat/Remind, badges ↻/🔔 dans les cellules date, drag & drop de reschedule préservé
- **20 tests** `tests/test_v58_calendar_reminders.py` ; suite complète **523 verte** ; `ruff check` OK
- **Version** — 5.11.7
### v5.9.0 — AI Writing Assist (éditeur) ✅ (2026-09-10)
> **Objectif** : l'IA Notion dans l'éditeur, au-dessus du moteur v4.10.0 (Agent IA). **COMPLETED**.
- [x] **Slash AI commands** — groupe « AI » dans le menu `/` : Write with AI, Summarize, Translate, Continue writing (`E.aiSlash`)
- [x] **Autocomplétion** — module `AIAC` : suggestion courte après ~900 ms d'inactivité, pastille « Tab » ancrée au bloc, insertion au `Tab`, rejet à `Escape`
- [x] **AI properties** — bouton ✨ par ligne de la table database : `POST /api/agent/writing/properties` propose Status/Priority/Résumé et applique les valeurs
- [x] **Service** `app/services/ai_writing.py` — 6 actions sans outils, replis déterministes hors-ligne
- [x] **Endpoints** — `POST /api/agent/writing` + `POST /api/agent/writing/properties`
- [x] **29 tests** `tests/test_ai_writing.py` ; version 5.9.0
---
## v5.10.0 — Éditeur : interactions de bloc ✅ (livré — voir section Completed)
## v5.11.0 — Wiki-links & mentions de page ✅ (2026-09-14)
> **Objectif** : le graphe de connaissances Notion. Complète les backlinks de v5.4.0
> (section « Lié depuis ») par la création des liens depuis l'éditeur. **COMPLETED**.
- [x] **Wiki-links `[[`** — taper `[[` ouvre le picker de pages (`GET /board/api/wiki/pages`, recherche substring + sous-séquence floue), Entrée insère un lien interne rendu comme chip atomique (icône + titre, cliquable) ; module `WM` dans `_page_editor_scripts.html` ; tokens `[[fdpage:ID]]` stockés dans le texte des blocs et relus intacts par `gtTok()` (autosave, drag, undo/redo préservés)
- [x] **Mention de page `@`** — le menu `@` (utilisateurs v4.9.0) gagne la section « Pages » : `@nom` cherche les pages et insère le chip inline
- [x] **Mention de date `@`** — section « Date » : `@today`, `@tomorrow`, `@hier` ou `@YYYY-MM-DD` insèrent `[[fddate:…]]`, rendus comme chips de date lisibles (« Fri 25 Dec 2026 »)
- [x] **Renommage propagé** — le token ne stocke que `page_id` ; `GET /board/api/wiki/titles` résout les libellés au rendu (editeur + page publique) → renommer une page met à jour tous ses liens ; page supprimée → « Deleted page »
- [x] Bonus — le scanner de backlinks v5.4.0 reconnaît les tokens wiki ; `_render_blocks_public` rend les chips en page publiée (HTML échappé) ; service `app/services/wiki_links.py` ; 15 tests dédiés (voir v5.12.0)
## v5.12.0 — Templates & verrouillage de page ✅ (2026-09-14)
> **Objectif** : démarrage rapide productif et protection des pages stabilisées.
> Les `page_templates` existent (v2.0.0/v4.2.0) mais uniquement côté collections. **COMPLETED**.
- [x] **Template picker global** — « + New page » (sidebar, footer, Ctrl+K) ouvre la galerie : 5 templates built-in (`app/services/block_templates.py` — Empty, Meeting notes, Weekly report, To-do list, Project doc) + templates custom utilisateur (`page_global_templates`, migration 13, cloisonnés par `created_by`)
- [x] **Bouton « Use template »** — `POST /board/api/page-templates/{id}/use` (id 0 = built-in par clé) duplique le contenu des blocs dans une nouvelle page ; « Empty » retombe sur la création classique ; menu « … » → « 📑 Save as template » capture la page courante
- [x] **Page lock** — toggle 🔒 dans le menu « … » : bannière sticky read-only, blocs/titre non éditables ; serveur `POST /board/api/pages/{id}/lock` + garde `_ensure_page_editable` → 423 sur `PUT /api/pages/{id}` et `POST /api/pages/{id}/blocks` ; déverrouillage réservé au poseur du lock (`locked_by`) ou admin (403 sinon) ; indicateur visuel = bannière en tête de page
- [x] **Full-width mode** — toggle dans le menu « … », persisté par page (`pages.full_width`), `POST /board/api/pages/{id}/options`, classe CSS `.full-width`
- [x] **Small text / typo options** — toggle « Aa Small text » (`pages.font_small`), classe `.small-text` (taille réduite ; serif/mono : non demandé au-delà du compact — volontairement hors scope)
## v5.13.0 — Collaboration temps réel ✅ (livré — voir section Completed)
## v5.14.0 — Synced blocks ✅ (2026-09-15)
> **Objectif** : avancer depuis v6.0.0 un bloc Notion très utilisé (même contenu dans
> plusieurs pages, édité une fois). **COMPLETED**.
- [x] **Bloc `synced_block`** — table `synced_blocks` (source de vérité) + références par page ; slash command `/synced`
- [x] **Rendu** — ring rouge + badge « Synced » sur le bloc ; édition à un endroit => mise à jour partout (via rooms WS v5.13.0 si actives, sinon au reload)
- [x] **Unsync** — action « Unsync » qui convertit l'instance en copie indépendante
- [x] **Copy & sync across pages** — copier un bloc dans une autre page avec option « Paste and sync »
### v5.15.0 — Webhooks v2 ✅ (2026-09-21)
> **Objectif** : webhooks sécurisés, fiables et complets pour les intégrations tierces. **COMPLETED**.
- [x] **Signature HMAC SHA-256** — header `X-FlowDeck-Signature` sur chaque payload, vérification côté receveur
- [x] **Retries avec backoff** — 4 tentatives max, délais 2s / 10s / 60s, logs détaillés
- [x] **20+ nouveaux événements** — total ~50 événements (pages, blocs, utilisateurs, collections, workspaces, etc.)
- [x] **API v2 endpoints** — `/api/v2/webhooks/test-signature` (test HMAC), `/api/v2/webhooks/retry` (relancer les échecs)
- [x] **Tests** — 21 dédiés (`tests/test_webhooks_v2.py`) ; **7 tests d'intégration câblés** (retry échec→succès, échec après MAX_ATTEMPTS, event inconnu ignoré, aucun abonné, fan-out wildcard, flow complet signé bout en bout, `retry_due_deliveries` superseded→delivered) — fixture `db` = SQLite isolée par test + `httpx.MockTransport` + `RETRY_DELAYS` neutralisé ; suite complète **749 verts, 0 skip**
---
## v6.0.0 — PWA : Progressive Web App, offline ✅ (2026-09-18)
> **Objectif** : support hors ligne complet (manifest, service worker, IndexedDB,
> queue de mutations, sync serveur + résolution de conflits). **COMPLETED**.
- [x] **Manifest & icônes PWA** — `static/manifest.json`, `static/icons/*`, `scripts/generate_pwa_icons.py`
- [x] **Service Worker** — `static/sw.js` (precache shell, network-first HTML/API, page offline, Background Sync)
- [x] **IndexedDB client** — `static/js/offline.js` (`window.FlowOffline`) : queue, delta, flush, marqueurs dirty
- [x] **Endpoints sync** — `app/routers/sync.py` + `app/services/sync_engine.py` (`/api/v2/sync/delta|batch|status`)
- [x] **Migrations** — table `offline_sync_queue` + colonnes `sync_version` (triggers AFTER UPDATE)
- [x] **Conflits** — edit-edit (last-write-wins), edit-delete (page orpheline), create-create (« copie offline »)
- [x] **UI** — banner offline + pending count, badge de synchronisation, toasts, icône ⟳ sur pages dirty
- [x] **Durcissement** — max 100 mutations/batch, timeout 30 s, rétention queue 30 j
- [x] **Tests** — `test_sync.py`, `test_sync_migrations.py`, `test_service_worker.py`, `test_pwa_offline.py`, E2E `e2e/pwa_offline.spec.js`
- [x] **Doc** — section `/help` « Offline mode (PWA) »
---
## v6.1.0 — Granular Permissions ✅ (2026-09-19)
> **Objectif** : page-level, collection-level & property-level ACL + groupes + audit. **COMPLETED**.
- [x] **Page permissions** — modes `inherit|restricted|private` (`pages.permission_type`, `collections.permission_type`, `collection_pages.permission_type`), grants explicites user/group (`page_permissions` role viewer/commenter/editor/owner), héritage page→collection→workspace, 404 masqué pour non-grantees, owner/admin bypass
- [x] **Collection permissions** — `collection_permissions` + permission_type, `/db/{id}` et `/db/{id}/api` masqués (404→302), création page et delete collection gatés (viewer 403), editor bypass
- [x] **Property-level visibility** — `property_permissions` (viewer|editor), `GET /db/{id}/properties/api` filtré par `get_visible_properties()`, `GET /api/v2/collections/{id}/properties/visible` (visible/hidden), grant = owner collection uniquement
- [x] **Groupes réutilisables** — tables `user_groups` + `group_members` (workspace-scoped, UNIQUE(name)), CRUD `/api/v2/groups` + `/groups/{id}/members`, grant par `group_id` (page/collection/property), retrait membre révoque l'accès
- [x] **API** — `app/routers/permissions.py` : pages (list/mine, grant, batch, revoke, permission-type), collections (list/grant/revoke/type + visible), properties (list/grant/revoke), groups (list/create/update/delete + members), users picker, audit `GET /api/v2/audit/permissions`
- [x] **Guards** — `board.py` (`GET/PUT /board/api/pages/{id}`) + `collections.py` (`delete collection`, `GET/PUT/DELETE page`, `properties`), 403/404 conformes, is_admin/owner bypass + `_session_user()` (no admin fallback)
- [x] **PermissionManager** — `app/services/permission_manager.py` étendu : `_explicit_grant_role()` (best rank user+groups), `get_page/collection_permission()`, `can_view/edit_page|collection`, `can_view/edit_property()`, `get_visible_properties()`, groups, `log_permission_change()`, cache 60s + `invalidate()`
- [x] **Audit** — table `permission_audit_log` (resource_type, action grant/revoke/type_change/group_*), index, log sur tous les mutateurs, `GET /api/v2/audit/permissions` (owner/admin only, limit 500)
- [x] **Migration 18** — `migrations.py` : création 6 tables + 3 colonnes `permission_type` + indexes (idempotent)
- [x] **Tests** — `tests/test_v60_granular_permissions.py` **21 tests** (inherit/restricted/private, grant viewer/editor, revoke, batch, type via API, collection restricted+grant, property visibility/hidden, group inherits + revoke, audit, auth 401, validation 400/404)
## v6.2.0 — Web Clipper : extension navigateur ✅ (2026-09-19)
> **Objectif** : capturer n'importe quelle page web en page FlowDeck (article, sélection, bookmark, screenshot) depuis une extension Manifest V3 + API directe. **COMPLETED**.
- [x] **Extension Manifest V3** — `extension/` + `static/extension/` (manifest, `background.js`, `content.js`, `popup.html/js`, `clipper.css`, icônes 16/32/48/128, `flowdeck-clipper.zip` servi à `/static/extension/`)
- [x] **4 types de capture** — article (HTML complet → `sanitize_html` + `html_to_blocks`), sélection, bookmark (carte OG v5.5.0), screenshot (base64) ; cap 10 MB / 200 blocs
- [x] **Serveur** — `POST /api/v2/web-clipper/clip`, `GET /status`, `POST /auth/verify` (register device → token `fd_…` montré une fois), `GET /devices`, `DELETE /devices/{id}` + page HTML `GET /extensions` ; auth triple (session OU Bearer `api_tokens` OU Bearer `extension_devices` OU legacy `user_tokens`), rate-limit 50/h/device
- [x] **Tables migration 19** — `extension_devices`, `extension_clips` + index `idx_ext_*`
- [x] **Settings UI** — onglet Extensions (devices, clips count, revoke)
- [x] **16 tests** `tests/test_web_clipper.py` ; `VERSION` 6.2.0 ; wiring `app/main.py:50,158`
### v6.2.1 — Web Clipper polish ✅ (2026-09-20)
- [x] **Bouton flottant rond transparent draggable** — toggle d'affichage persistant, `clipper:clipped` refresh auto sidebar
- [x] **Fix bloc bookmark** — `create_page_from_clip()` émet un bloc `bookmark` fidèle (OG + `embed_src` résolu), rendu/correct en éditeur + `/p/<slug>` + exports
- [x] Fix `duplicate inner` SyntaxError dans `_page_editor_scripts.html`
- [x] `flowdeck-clipper.zip` régénéré
## v6.3.0 — API publique complète v2 ✅ (2026-09-21)
> **Objectif** : REST API documentée OpenAPI, CRUD complet, un seul chemin de code (wrappers sur les services internes). Parité `docs/API_GUIDE_V6.md` §4 (~80 endpoints) + scopes hiérarchiques `read < write < admin`. **COMPLETED**.
> **Route** : `feat/v6-api-v2` → `develop` → `main` — livraison **en une fois** (tous domaines).
> **Doc** : [`docs/API_GUIDE_V6.md`](/docs/API_GUIDE_V6.md) · OpenAPI : `/docs` + `docs/openapi-v2.json` (402 chemins)
#### Phase 0 — Roadmap & doc catch-up ✅
- [x] Tagguer v6.2.0/v6.2.1 dans `CHANGELOG.md` + `ROADMAP.md` + `docs/V6_Web_Clipper.md` → `COMPLETED`
- [x] Détailler v6.3.0 phases 1-8 dans `ROADMAP.md` (plan gelé)
#### Phase 1 — Migrations socles (v20) ✅
- [x] `api_tokens` : colonnes `scopes TEXT DEFAULT 'read,write'`, `expires_at TIMESTAMP` (idempotent, rétro-compat)
- [x] `webhook_deliveries` : `id, webhook_id FK, status, http_code, error, duration_ms, attempt, created_at`
- [x] `api_audit_log` : `id, user_id, token_id, action, resource_type, resource_id, ip, created_at`
- [x] `idempotency_keys` : `key TEXT PRIMARY KEY, user_id, response_json, created_at`
#### Phase 2 — Helpers & auth v2 unifiée (scopes hiérarchiques) ✅
- [x] `app/config.py` : `public_api_insecure_ok: bool = False` — `fd-public-key` accepté seulement si `True` (dev local)
- [x] `app/services/api_v2_helpers.py` : `parse_pagination()` (+`X-Total-Count`), `to_iso8601()`, handler RFC 7807 `application/problem+json`, `require_scope()` (hiérarchie `admin ⊇ write ⊇ read`), `resolve_bearer_token()` / `get_bearer_user()` (hash sha256, `revoked` + `expires_at` + scopes, `last_used_at`, `extension_devices`)
- [x] Auth Bearer unifié partagé (clipper + legacy `user_tokens` supportés) ; handler d'erreurs unifié sur `StarletteHTTPException`
#### Phase 3 — Tokens CRUD v2 ✅
- [x] `POST /api/v2/tokens` (`name, scopes, expires_at`) → `fd_{urlsafe(32)}` hashé, montré une fois
- [x] `GET /api/v2/tokens` (prefix only), `DELETE /api/v2/tokens/{id}` (revoke), `POST /api/v2/tokens/{id}/rotate`
#### Phase 4 — Wrappers read (pagination/filtres/tri/fields) ✅
- [x] `app/routers/api_v2.py` (`prefix="/api/v2"`, tag `api-v2`) — `GET /collections`, `GET /collections/{id}`, `GET /pages/{id}`, `GET /collections/{id}/pages?filter[]=&sort=&fields=&query=`, `GET /search?query=&workspace_id=&type=` (FTS5), header `X-Total-Count`, `filter[]` AND, `sort=prop/-prop`
#### Phase 5 — Wrappers write critiques (`Idempotency-Key`) ✅
- [x] Collections : `POST/GET/PATCH/DELETE /collections/{id}` + `/linked`, `/task`, `/sources`
- [x] Pages : `POST/GET/PATCH/DELETE /pages/{id}` + `/restore`, `/move`, `/sub-items`, `/dependencies`
- [x] Properties : `GET/POST /collections/{id}/properties`, `PATCH/DELETE /properties/{id}`, `POST .../relation`, `evaluate-formula`, `compute-rollup`
- [x] Views/Dashboards/Comments/Notifications/Favorites/Tags/Recents/Sharing/History/Sprints/Templates/Export/Workspaces/Users/Admin — regroupés par ressource
#### Phase 6 — Webhooks v2 — CRUD simple ✅
- [x] `GET/POST/PATCH/DELETE /api/v2/webhooks`, `POST /api/v2/webhooks/{id}/test` (ping)
- [x] `GET /api/v2/webhooks/{id}/deliveries` (journal basique)
- [x] *(reporté v6.4, livré plus tôt)* **v5.15.0** : signature HMAC `X-FlowDeck-Signature`, retry 2s/10s/60s, +20 events (Webhooks v2)
#### Phase 7 — Forges & ressources restantes ✅
- [x] Sprints, dashboards, templates, export/import, workspaces/members, users, admin
- [x] Forges : `GET /projects`, `/projects/{owner}/{repo}/tree` (best-effort via `gitea_client`)
- [x] *(reporté, livré v6.4.0)* : migration de `sync.py` vers Bearer — auth `Authorization: Bearer` (scopes read/write) via `get_bearer_user()` + repli cookie session conservé pour compat PWA offline (`app/routers/sync.py`)
#### Phase 8 — OpenAPI, tests & docs ✅
- [x] `docs_url="/docs"` + `redoc_url="/redoc"` activés ; `docs/openapi-v2.json` généré (402 chemins)
- [x] `tests/test_public_api_v2.py` — **24 tests** (auth scopes, pagination, filtres, RFC 7807, idempotency, webhooks deliveries, CRUD multi-domaines)
- [x] Vérif `ruff check app tests` + `pytest -n auto` → **668 verte**
## v6.4.0 — Realtime editing (production) ✅ (2026-09-22)
> **Objectif** : passer le realtime v5.13.0 en « production » — résolution de
> conflits au-delà du last-write-wins + édition à grande échelle (broadcast non
> bloquant, plusieurs rooms/pages, observabilité). **COMPLETED**.
#### Conflits au-delà du LWW
- [x] **Merge à 3 voix (diff3-lite)** — nouveau service `app/services/realtime_merge.py` : `merge_text_3way()` (merge de caractères) + `merge_block_3way()` (merge champ-par-champ), fonctions pures et testées sans WebSocket ni base
- [x] **Régions disjointes conservées** — deux utilisateurs tapant à des endroits différents du *même* bloc voient leurs deux saisies survivre (au lieu d'écraser l'une par l'autre) ; ordre d'arrivée indifférent
- [x] **Chevauchement réel → LWW par champ + drapeau** — un conflit n'est plus « tout le bloc perdu » mais limité au champ concerné ; `conflict: true` renvoyé dans l'`ack` et le broadcast
- [x] **Protocole `base`** — le client embarque dans chaque `update` la version du bloc dont dérive sa saisie ; le serveur fait `merge_block_3way(base, current, incoming)` ; sans `base` → LWW historique (rétro-compat ancien client)
- [x] **Adoption côté client** — l'`ack` renvoie le bloc fusionné ; le client met à jour sa `base`, adopte le résultat (hors bloc en cours d'édition) et affiche un toast en cas de conflit
- [x] **Broadcast du résultat fusionné** — le serveur diffuse toujours le bloc final fusionné (jamais la proposition brute) pour convergence garantie de tous les clients
#### Échelle & robustesse
- [x] **Broadcast non bloquant** — chaque connexion a une file sortante (`asyncio.Queue`) + une tâche `_writer` dédiée ; `_broadcast()` fait `put_nowait` et n'attend plus le socket → un client lent ne fige plus la room
- [x] **Coalescence des curseurs** — le writer réduit les messages `sel` empilés à la position la plus récente (seule la dernière compte), tout en préservant l'ordre des messages importants
- [x] **Déconnexion des clients trop lents** — file pleine (`MAX_OUT_QUEUE=512`) → fermeture 4413 + compteur `slow_disconnects` (évite qu'une room entière stagne sur un pair mortel)
- [x] **Anti-flood** — budget d'opérations par connexion (`OP_WINDOW_MAX=400` / 10 s) ; au-delà, réponse `ack stale` sans application
- [x] **Fix fuite de rooms** — une connexion 4404 n'enregistre plus de `Room` orpheline en mémoire ; `room_state()` sur page inexistante retourne un dict vide au lieu de planter sur `None`
- [x] **Observabilité** — `GET /api/realtime/stats` (authentifié) : rooms, connexions, ops, merges, conflits, déconnexions lentes + détail par page
Détails livrés :
- `app/services/realtime_merge.py` — merge 3-voix (nouveau, ~170 lignes)
- `app/services/realtime_server.py` — `RTConn` (file + writer + budget), `_apply()` avec merge, `_evict_slow()`, `stats()`, fix fuites
- `app/routers/realtime.py` — endpoint `GET /api/realtime/stats`
- `app/templates/_page_editor_realtime.html` — envoi de `base`, adoption du bloc fusionné, toast de conflit
- **26 tests** `tests/test_realtime_v64.py` (merge purs, protocole WS, convergence 2 clients, rétro-compat LWW, fuite 4404, coalescence, stats, anti-flood) ; **14 tests** `tests/test_realtime.py` préservés
- `ruff check app tests` OK · `eslint static/js` 0 problème
- [x] **Version** — 6.4.0
---
## v6.5.0 — Synced blocks production (databases & vues) ✅ (2026-09-24)
> **Objectif** : passer les synced blocks en « production » (résolution +
> propagation fiables partout) et les faire vivre dans les databases/vues —
> le « reste en v6 » du point **v5.14.0**. **COMPLETED**.
#### Contenu des lignes de database (le point « databases »)
- [x] **Migration 22** — `pages.collection_row_id INTEGER REFERENCES collection_pages(id) ON DELETE CASCADE` + index partiel ; chaque ligne de database gagne une **page contenu** (page `blocks` standard) portant son éditeur complet
- [x] **Service `app/services/row_pages.py`** — `ensure_row_page()` : création lazy/réparation (page trashée restaurée, `parent_section='DbRow'`), workspace hérité de la page hôte (DB full-page → page parente inline → `collections.gitea_owner/repo`), `collection_id` hérité pour que les ACL de collection s'appliquent au contenu
- [x] **Endpoint `GET /db/pages/{row_id}/open/api`** — renvoie `{page_id}` (garde `_require_view` de la collection) ; utilisé par le peek de **toute** vue (table/board/gallery/list/calendar)
- [x] **Fix bug d'ouverture de ligne** — le peek appelait `/pages/{rowId}` qui lit la table `pages` → avec des ids croisés il ouvrait une **page au hasard** (ligne id 5 « Ask AI » → page id 5 « Données ») ; désormais résolution via l'endpoint puis iframe `/pages/{pageId}`
- [x] **Titres synchronisés bidirectionnellement** — renommer la ligne (`PUT /db/pages/{id}/api`) → page contenu ; renommer dans l'éditeur (`PUT /board/api/pages/{id}`, `POST .../blocks`, `PUT /api/pages/{id}/rename`) → ligne
- [x] **Cascade** — supprimer une ligne supprime sa page contenu (FK `ON DELETE CASCADE`, refs `page_synced_blocks` en cascade)
- [x] **Exclusions des listings** — `AND collection_row_id IS NULL` sur : arbre sidebar (`_build_page_tree`), racines workspace (`_load_workspace_pages`), liste des pages du board, projets builtins, arbre Library (`_build_tree_children` + API tree), `page_count` des workspaces ; `parent_section='DbRow'` les tient hors des sections Private/Trash
#### Résolution serveur + propagation (le point « production »)
- [x] **Résolution à chaque lecture** — `resolve_content_json()` (`app/services/synced_blocks.py`) appliqué sur : les 2 routes de rendu `/pages/{id}` (dashboard + board, dual-route), `GET /api/pages/{id}/content`, `GET /api/local-workspace/page-content/{id}`, page publique `/p/{slug}`, `GET /api/v2/pages/{id}`, et les rooms realtime (`load_room`) → le cache `_synced_content` périmé n'est plus jamais servi
- [x] **Propagation écrite réelle** — `PUT /api/synced-blocks/{id}` réécrit le contenu stocké de **chaque** page référente (`sync_synced_blocks_in_page`, jusqu'ici jamais appelé = code mort depuis v5.14.0) **avant** le broadcast WS ; récursif dans les `children` (colonnes, toggles)
- [x] **Suppression de source** — ids des pages référentes collectés **avant** la cascade FK, `mark_synced_block_deleted()` marque `_synced_deleted` dans le stocké, broadcast dédié `manager._broadcast_synced_to()`
- [x] **État « deleted »** — `resolve_synced_block()` marque `_synced_deleted` (source absente) ; rendu dédié dans l'éditeur (« Deleted synced block » au lieu d'un cache fantôme / « Loading… » éternel)
- [x] **Rendu public des synced blocks** — branche `synced` ajoutée à `_render_blocks_public` (avant : JSON brut en `<p>`) + état deleted
- [x] **API v2** — `content_page_id` sur `GET /api/v2/pages/{id}` pour une ligne (sans création lazy en lecture)
#### Tests
- [x] **`tests/test_v65_synced_db.py` — 17 tests** (migration, création/idempotence/cascade/restauration de la page contenu, sync des titres, exclusions de listings, résolution lecture éditeur/content/public, propagation écrite + récursivité + deleted, synced block dans une ligne de DB de bout en bout, `content_page_id` v2)
- [x] Suite complète **742 verts** (`pytest -n auto`) · `ruff check app tests` OK · `eslint static/js` 0 problème
- [x] **Version** — 6.5.0 (VERSION + `app/main.py`)
---
## v6.0.0 — Pro (futur)
- [x] **PWA** — Progressive Web App, offline support ✅ (livré) — [📄 Conception détaillée](/docs/V6_PWA_Progressive_Web_App.md)
- [x] **Granular permissions** — page-level, property-level access control ✅ (livré v6.1.0) — [📄 Conception détaillée](/docs/V6_Granular_Permissions.md)
- [x] **Web Clipper** — extension navigateur ✅ (livré v6.2.0/6.2.1) — [📄 Conception détaillée](/docs/V6_Web_Clipper.md)
- [x] **API publique complète** — REST API documentée (OpenAPI) ✅ (livré v6.3.0) — [📄 API Guide v2](/docs/API_GUIDE_V6.md) · [📄 OpenAPI](/docs/openapi-v2.json)
- [ ] **SSO/SAML** — enterprise authentication — [📄 Conception détaillée](/docs/V6_SSO_SAML_Enterprise_Auth.md)
- [x] **Realtime editing (production)** ✅ livré **v6.4.0** (merge 3-voix au-delà du LWW, broadcast non bloquant) ; voir **v5.13.0** pour le socle (curseurs + présence)
- [x] **Synced blocks (production)** ✅ livré **v6.5.0** (page contenu par ligne de database, résolution serveur à chaque lecture, propagation écrite réelle, état deleted, rendu public) ; socle : **v5.14.0** (bloc de base)
---
## ✅ Fonctionnalités livrées hors roadmap (bonus détectés dans le code)
| Feature | Fichiers | Note |
|---------|----------|------|
| Webhooks sortants | `services/webhook_outbound.py`, `routers/workspace.py` (`/workspace/webhooks`) | CRUD + dispatch d'événements — base pour automations/API publique |
| API publique + tokens | `routers/public.py` / `public_api.py`, test `test_public_api_token` | À formaliser dans v5.2.0 et documenter pour v6.0.0 |
---
## 🎯 Ordre de priorité recommandé (état 2026-09)
1. ~~**v5.2.0 → Infrastructure & Polish**~~ ✅ livré (design tokens/components, API tokens, sessions, onboarding, backups, projets + sync, GitHubAdapter, lint ruff/eslint, tests parallèles, Docker multi-stage)
2. ~~**v5.0.0 → Command palette + FTS5**~~ ✅ livré (palette Ctrl+K + `GET /api/search`)
3. ~~**v5.3.0 → Inline databases + templates + validation**~~ ✅ livré (slash `/database`, 6 templates, validation propriétés)
4. ~~**v5.13.0 → Realtime (WS + présence)**~~ ✅ livré (`app/services/realtime_server.py` + `WS /ws/pages/{id}`, présence, curseurs live, merge LWW, 12 tests)
5. ~~**v5.10.0 → Interactions de bloc**~~ ✅ livré (drag&drop multi, undo/redo, duplicate, menu ⋮, en-têtes de tableau, 9 tests)
6. ~~**v5.4.0 → Expérience éditeur**~~ ✅ livré (backlinks, page/collection duplicate, corbeille globale + purge 30 j, historique de version UI, import Markdown/CSV/Notion)
7. ~~**v5.5.0 → Embeds & Média riche**~~ ✅ livré (embed universel 15 providers, bookmark cards OG, lightbox clavier, préviews PDF/vidéo/audio, cover & icône ; 47 tests dédiés)
8. ~~**v5.6.0 → Import de données (6 phases)**~~ ✅ **livré** — Phase 0 socle unifié · Phase 1 notes/Markdown (Obsidian, Notion, Logseq/Roam, Apple Notes/Bear, Google Keep, OneNote) · Phase 2 données/tableaux (CSV typé, Excel, Sheets, JSON) · Phase 3 documents (Word, Google Docs, HTML, PDF) · Phase 4 signets/dev/divers (Gitea/GitHub, Raindrop, Pocket, Readwise, Shaarli, `.ics`, OPML, Standard Notes) · Phase 5 durcissement (re-sync, dépôt forge, URL/web clipper, lot multi-fichiers, relations Notion, rapports exportables)
9. ~~**v5.7.0 → Database Avancée (Pt. 2)**~~ ✅ **livré** (person + auto-propriétés, groupes de propriétés, vues sauvegardées par utilisateur, swimlanes, WIP limits, cartes configurables, calendar drag & drop, gallery couvertures ; 12 tests dédiés)
10. ~~**v5.8.0 → Calendrier & Rappels**~~ ✅ **livré** (vues jour/semaine/mois, récurrences RRULE expandues serveur, rappels in-app + email avec dédup, fuseaux par utilisateur/événement, notifications d'assignation, template Meeting notes enrichi ; 20 tests dédiés)
11. ~~**v5.11.0 → Wiki-links & mentions de page**~~ ✅ **livré** (picker `[[`, mentions `@` pages/date, chips atomiques, renommage propagé, backlinks wiki, chips en page publique)
12. ~~**v5.12.0 → Templates & verrouillage de page**~~ ✅ **livré** (template picker global 5 built-in + templates perso, use-template, page lock 423, full-width, small text)
13. **v5.14.0 → v5.14.0 COMPLETED** ✅ (synced blocks)
---
## Résumé des phases
```
v0.x–v1.x ✅ v2.x ✅ v3.0.0 ✅ v4.0.0 ✅ v4.0.1 ✅
Base + Kanban Éditeur + Gitea UX Pro MVP Onboarding
+ DB + Auth + Workspaces + Multi-select Accounts & Polish
+ UI Notion + Tags + Admin + Sharing COMPLETED
+ GitHub OAuth + Library
v4.0.2 ✅ v4.1–4.9 ✅ v4.10 ✅ v5.0–5.3 ✅ v5.13 ✅ · v5.10 ✅ v5.5 ✅ · v5.7 ✅ v5.14 ✅ · v6.0 ✅
Quality DB views, Agent IA Palette → Realtime + Embeds & Synced PWA · Perms
& Tests Templates & COMPLETED Automations Interactions Média riche blocks Clipper · API v2
Collaboration Realtime, de bloc (undo/ (embed, + DB
DB avancée, redo, drag&drop, bookmark, avancée
Calendrier, AI duplicate) lightbox…) (Pt.2) v6.1 ✅ v6.2 ✅ v6.3 ✅
*Dernière mise à jour: 2026-09-24 — **v6.5.0 Synced blocks production (databases & vues) COMPLETED** (page contenu par ligne de DB + ouverture correcte des lignes dans les vues, résolution serveur à chaque lecture, propagation écrite réelle, état deleted, rendu public des synced blocks ; 17 tests dédiés) + **v6.4.0** realtime + **v6.3.0** API v2. Aussi: case `sync.py` → Bearer cochée (livrée v6.4.0) + 7 tests webhooks_v2 dé-skipés → **suite 749 verts, 0 skip**. Reste: **SSO/SAML**.*