> **IMPORTANT — Lire d'abord** > Ce document utilise un **format tabulaire simple et rigoureux** conçu pour être > maintenu à la fois par un humain (éditeur texte) et par un agent IA. Les règles > exactes sont définies dans la section **« Comment fonctionne ce document »**. > La procédure de livraison obligatoire (tests, docs, commit, push, CI) est définie > dans [`DELIVERY_WORKFLOW.md`](./DELIVERY_WORKFLOW.md). # 🐛 ObsiGate — Suivi des Bugs / TODO de Correction > **Rôle du document** : registre unique et vivant des bugs découverts et des corrections > à apporter au projet **ObsiGate** (application Web + desktop Tauri). > C'est l'**outil de communication commun** entre l'utilisateur humain et l'IA > qui effectue les corrections dans le code. - **Projet** : ObsiGate — Porte d'entrée web pour vaults Obsidian - **Stack** : Python 3.11+ (backend FastAPI) · JavaScript/Vanilla (frontend) · Tauri/Rust (desktop) - **Dernière mise à jour** : 2026-09-17 --- ## ⚙️ Comment fonctionne ce document (À LIRE ABSOLUMENT) Ce document est un **contrat de travail partagé**. Suivez les règles ci-dessous pour qu'un humain OU un agent IA puisse le lire, le modifier et l'exploiter sans ambiguïté. ### 1. Une table = un état (workflow par rangée) Chaque bug/TODO est une **rangée** dans la section [📋 Registre des bugs / TODOs](#-registre-des-bugs--todos). Une rangée ne change **jamais de table** au fil de sa vie ; c'est la **colonne « Statut »** qui encode son avancement : | Statut (colonne) | Signification | Qui peut le poser | |---|---|---| | `🔴 ouvert` | Bug confirmé / tâche à faire, en attente de traitement | Utilisateur **ou** IA | | `🟠 en cours` | Un agent IA a **commencé** à corriger | IA uniquement | | `🟢 corrigé` | La correction est écrite **et** vérifiée (tests OK) | IA uniquement | | `✅ vérifié` | L'utilisateur (ou les tests) a **validé** le correctif | Utilisateur uniquement | | `⚪ abandonné` | Décision de ne pas corriger (documentée dans notes) | Utilisateur ou IA | ### 2. Cycle de vie d'un bug (workflow) ```mermaid flowchart LR A[🔴 ouvert
Découverte d'un bug] --> B[🟠 en cours
IA travaille dessus] B --> C[🟢 corrigé
Correctif écrit + tests OK] C --> D[✅ vérifié
Re-validation par l'utilisateur] C --> E[⚪ abandonné
Refus / hors périmètre] D --> F[✅ Fermé : lignée conservée en historique] ``` 1. **Un bug est signalé** → posé en `🔴 ouvert` par l'utilisateur **ou** l'IA (à l'issue d'une investigation ou d'une session de test). 2. **L'IA prend en charge** → passe le statut en `🟠 en cours` **avant** de commencer à coder, et crée une entrée dans [🛠️ Journal des interventions AI](#-journal-des-interventions-ai). 3. **La correction est terminée** → l'IA exécute les tests pertinents, passe le statut en `🟢 corrigé` et remplit la colonne « Correctif / Commit ». 4. **Validation finale** → c'est l'utilisateur qui passe en `✅ vérifié` une fois le correctif contrôlé. Les rangées `✅ vérifié` sont **déplacées dans l'historique** périodiquement. 5. **Abandon** → justifié obligatoirement dans la colonne « Notes / Scope ». > ⚠️ **Ne JAMAIS supprimer** une rangée terminée : déplacez-la dans la section > [📜 Historique des bugs résolus](#-historique-des-bugs-résolus) à la place. ### 3. Ordre de lecture pour un agent IA Avant de corriger quoi que ce soit, un agent IA doit : 1. **Lire ce fichier en entier** (en particulier ce guide et le registre). 2. **Identifier** les rangées de statut `🔴 ouvert` avec un **scope `IA`** et une **priorité** élevée qui lui sont attribuées (colonne « Assigné »). 3. **Poser le statut `🟠 en cours`** sur la rangée choisie **avant** toute modification de code. 4. Corriger en respectant la [checklist de vérification](#-checklist-de-traitement-dun-bug-rappel-pour-toute-ia) et les [conventions du projet](CONTRIBUTING.md). 5. Revenir mettre le statut à `🟢 corrigé`, documenter le correctif, et **journaliser** son passage. 6. Ne pas marquer `✅ vérifié` lui-même (c'est un droit utilisateur, sauf mention contraire explicite). ### 4. Identifiants uniques (ID) - Chaque bug porte un **ID stable** `BUG-###` ou `TODO-###` (voir le format dans le registre). - L'ID **ne change jamais**, même après résolution. Il permet de retrouver le bug dans l'historique et dans le journal d'interventions. - Le format du numéro est `###` = 3 chiffres incrémental (001, 002, 003…). - Si vous (IA) créez un nouveau bug, **reprenez le plus grand numéro existant + 1** dans le registre, quel que soit l'ordre des lignes. ### 5. Comment mettre à jour ce document (bonnes pratiques) - **Formatage** : garder les colonnes alignées par des espaces pour la lisibilité brute. Une rangée = une seule ligne de tableau Markdown. - **Copier-coller** : pour ajouter un bug, dupliquez une ligne existante puis modifiez les champs. - **Ne pas casser** : conservez le séparateur d'en-tête `|---|---|…|` et les **champs fixes** (#, Titre, Statut, Priorité, Scope, Assigné, Zone, Cmd de repro, Correctif/Commit, Notes). - **Frontière des rôles** : un agent IA ne doit **jamais** se passer à lui-même une rangée en `✅ vérifié` ; il s'arrête à `🟢 corrigé` (sauf si l'utilisateur l'y autorise explicitement). --- ## 📋 Registre des bugs / TODOs > **Légende colonnes :** > - **Statut** : `🔴 ouvert` | `🟠 en cours` | `🟢 corrigé` | `✅ vérifié` | `⚪ abandonné` > - **Priorité** : `P0` (critique/bloquant) · `P1` (élevée) · `P2` (normale) · `P3` (basse/cosmétique) > - **Scope** : `📱 frontend` · `⚙️ backend` · `🔌 api` · `🧩 tests` · `🖥️ desktop` · `📦 build` · `📄 docs` · `🤖 ia` · `❓ inconnu` (À affiner par l'IA) > - **Assigné** : `IA` (à traiter par un agent) · `USER` (à traiter par l'utilisateur) · `—` (non attribué) > - **Zone** : chemin/nom de fichier concerné (ex. `frontend/app.js`, `backend/search.py`) > - **Cmd de repro** : commande ou scénario permettant de reproduire / vérifier (vide si N/C) ### Bugs ouverts (à traiter) | # | Titre | Statut | Priorité | Scope | Assigné | Zone (fichier) | Cmd de repro | Correctif / Commit | Notes | |---|---|---|---|---|---|---|---|---|---| | *BUG-001* | L'ouverture des fichier PDF ne fonctionne pas et donne l'erreur Internal Server Error | 🟢 corrigé | P1 | fichier PDF | IA | `backend/main.py` | `GET /api/file/{vault}/pdf/stream?path=…(pdf à nom accentué)` | `backend/main.py` : Content-Disposition encodé RFC 5987 (helper `_content_disposition`) | 500 car nom Unicode brut dans l'en-tête → header invalide. Vérifié: stream 200 / Range 206 + test `tests/test_pdf_stream.py` | | *BUG-002* | l'ouverture d'un fichier .excalidraw ne fonctionne pas et affiche toujours Loading *Excalidraw…* | 🟢 corrigé | P1 | fichier .excalidraw | IA | `frontend/excalidraw-editor.html` | Ouvrir un fichier `.excalidraw` | `frontend/excalidraw-editor.html` : alias esm.sh supprimé (408 jotai) + React 19 cohérent + prop `excalidrawAPI` | 2 causes: 408 esm.sh sur `?alias` + prop legacy `excalidrawRef` inopérante en 0.18. Vérifié navigateur: Loading masqué + cycle save OK | | | | | | | | | | | | | *BUG-003* | `mypy` : 33 erreurs de typage (étape CI en mode advisory → bloquante) | 🟢 corrigé | P2 | ⚙️ backend | IA | `backend/{main,indexer,export,pdf_reader,bookslm_routes}.py`, `backend/auth/router.py`, `.gitea/workflows/ci.yml` | `mypy backend/ --ignore-missing-imports` | Annotations de types, gardes `None` (auth/router), import `PROVIDERS` manquant (bug latent `main.py:4523`), `PdfReader: Any` ; CI mypy rendu bloquant | 0 erreur après correction. `PROVIDERS` non importé → `NameError` avalé par le `except` (le modèle par défaut n'était jamais prépendé). Tests : 728 passed | | *BUG-004* | Lien cassé vers `CONTRIBUTING.md` dans `README.md` | 🟢 corrigé | P3 | 📄 docs | IA | `README.md` | Cliquer le lien « Contributing » | `README.md` : lien → `docs/CONTRIBUTING.md` (+ `DELIVERY_WORKFLOW.md`) ; arbre du projet corrigé | `README.fr.md` pointait déjà correctement vers `./docs/CONTRIBUTING.md` | | *BUG-005* | Sur mobile via Cloudflare (`og.dracodev.net`), le site ne charge pas complètement même après purge des caches (téléphone + Cloudflare) | 🟢 corrigé | P1 | 📱 frontend + ⚙️ backend | IA | `frontend/sw.js`, `frontend/index.html`, `frontend/js/sync.js`, `backend/main.py` | Ouvrir `og.dracodev.net` sur mobile (PWA installée), purger les caches, recharger | SW **network-first** pour le code + caches versionnés (`SW_VERSION`) ; précache corrigé (`/static/js/…`) ; migration ponctuelle `localStorage` ; `Cache-Control: no-cache` (le middleware renvoyait `immutable` 1 an sur `/static`) ; tests `tests/frontend/sw.test.mjs` + `TestStaticCaching` | Cause : SW cache-first à nom fixe + en-têtes `immutable` non fingerprinted → ancien build servi indéfiniment (Cache Storage ≠ cache navigateur/CF). Vérifié : 754 tests backend, frontend OK | | *BUG-006* | Clé API DeepSeek réelle commitée en clair dans `.env.example` | 🟢 corrigé | P0 | 📄 docs + 🔐 sécurité | IA | `.env.example` | `git grep "sk-" .env.example` | `.env.example` : clé remplacée par un placeholder (`sk-xxx…`) + modèle `deepseek-chat` | ⚠️ **Rotation de la clé requise** : elle reste dans l'historique Git → révoquer/régénérer côté DeepSeek. Le fichier `.env` (réel) est bien gitignoré | | *BUG-007* | Assistant IA : menus `/` et `@` — navigation clavier ↑/↓ inopérante et filtrage des skills cassé par les accents | 🟢 corrigé | P1 | 📱 frontend | IA | `frontend/js/bookslm.js` | Taper `/résumé` ou `@café`, puis ↑/↓ | `bookslm.js` : motifs Unicode `\p{L}` (détection + sélection), recherche normalisée NFD (insensible aux accents), jeton `_menuSeq` (rendus async obsolètes), `scrollIntoView` de l'élément actif | Le menu se fermait dès la saisie d'un accent ; la frappe rapide pouvait écraser le menu avec un résultat obsolète. Tests : `tests/frontend/ai.test.mjs` (+3) | | *BUG-008* | Assistant IA : commande `@` — chemins accentués + contexte ad-hoc ignoré en mode Général | 🟢 corrigé | P1 | 📱 frontend + ⚙️ backend | IA | `frontend/js/bookslm.js`, `backend/bookslm_routes.py` | `@fichier-accentué` puis envoyer en mode Général | Frontend : détection Unicode + repli `vault=all` sans vault courant. Backend : `_resolve_system_prompt` résout le vault optionnel dès qu'un contexte `@` est présent (mode Général) | Le backend ignorait `extra_files`/`extra_directories` en mode Général (`vault_path is None`). Tests : `tests/test_bookslm.py` (+2) et `tests/frontend/ai.test.mjs` | | *BUG-009* | Assistant IA : liste de modèles corrompue (art ASCII) et clés i18n brutes (`ai.model_search`) sous OpenRouter | 🟢 corrigé | P1 | 📱 frontend | IA | `frontend/js/ai.js`, `frontend/js/i18n.js`, `frontend/sw.js`, `frontend/style.css` | Ouvrir le menu modèle sous « openrouter » | Locale non content-hashée en cache HTTP → `cache:'no-store'` + bump `SW_VERSION` v4 ; styles critiques du picker appliqués **en ligne** (popover absolu, liste en colonne, options `display:block`) ; rendu plafonné à 200 modèles + indicateur « … N autres » | OpenRouter expose plusieurs centaines de modèles ; si `style.css` est en cache, la liste s'affichait en bloc inline (art ASCII) et les clés i18n brutes provenaient d'un `fr.json` obsolète. Tests : `tests/frontend/ai.test.mjs` (+1) | | *BUG-010* | Assistant IA : la commande `@` n'ajoute pas le contexte à la requête en mode Général | 🟢 corrigé | P1 | 📱 frontend | IA | `frontend/js/bookslm.js` | Assistant en mode Général, taper `@fichier`, sélectionner, envoyer | Capture du `vault` renvoyé par `/api/tree-search` dans la sélection `@` ; `_contextVault()` propage ce vault aux requêtes `/context` et `/chat` ; recherches `@` suivantes limitées à ce vault | En mode Général, `_vault` est nul : la requête partait sans vault et le backend ignorait `extra_files`. Tests : `tests/frontend/ai.test.mjs` (+1) | | *BUG-011* | Assistant IA : noms de modèles illisibles dans la liste déroulante | 🟢 corrigé | P2 | 📱 frontend | IA | `frontend/js/ai.js`, `frontend/style.css` | Ouvrir le menu modèle dans la barre latérale de l'assistant | Popover aligné à droite du déclencheur (`right:0`), largeur 340 px bornée à `100vw - 24px`, noms sur plusieurs lignes (`overflow-wrap:anywhere`, police 0,78 rem) + `title` | La liste s'ouvrait vers la droite et dépassait le bord de la sidebar ancrée à droite ; noms tronqués. Tests : `tests/frontend/ai.test.mjs` | | *BUG-012* | Assistant IA : la commande `@` n'affiche aucun menu de sélection en mode Général | 🟢 corrigé | P1 | 📱 frontend | IA | `frontend/js/bookslm.js` | Assistant en mode Général (aucun vault), taper `@` | `_showMentionMenu` rend **toujours** le menu (même vide) et `_activeVault()` retombe sur `state.selectedContextVault` puis le premier `state.allVaults`, avant `vault=all` pour les requêtes | Sans vault résolu, l'ancien code masquait le menu et le backend ignorait le contexte. Tests : `tests/frontend/ai.test.mjs` (+1) | | *BUG-013* | Assistant IA : les liens fichiers/répertoires des réponses affichent « Aucun vault actif pour ouvrir ce lien » | 🟢 corrigé | P1 | 📱 frontend | IA | `frontend/js/bookslm.js` | En mode Général, cliquer un lien de fichier/dossier dans une réponse | `_openFileLink()` et `_revealPath()` utilisent `_activeVault()` (vault de contexte → vault sélectionné → premier vault) au lieu de `_resolveVault()` seul | `_resolveVault()` est nul sans document ouvert ; les liens échouaient en mode Général. Tests : `tests/frontend/ai.test.mjs` (+1) | | *BUG-014* | Assistant IA : les flèches ↑/↓ ne naviguent pas dans les menus `/` et `@` | 🟢 corrigé | P1 | 📱 frontend | IA | `frontend/js/bookslm.js` | Ouvrir `/`, puis ↑/↓ (y compris après un clic hors zone de texte) | Navigation gérée au niveau du **panneau en phase de capture** (`panel.addEventListener('keydown', ..., true)`), donc indépendante du focus | Le `keydown` n'était écouté que sur la zone de texte ; dès que le focus changeait, les flèches étaient ignorées. Tests : `tests/frontend/ai.test.mjs` (+1) | | *BUG-015* | Assistant IA : la ligne sélectionnée des menus `/` et `@` est invisible au clavier | 🟢 corrigé | P2 | 📱 frontend | IA | `frontend/style.css` | Ouvrir `/` ou `@`, naviguer avec ↑/↓ | État `.active` en `--bg-hover` + barre d'accent à gauche (`inset 3px 0 0 var(--accent)`) au lieu de `--surface2` | `--surface2` est identique à `--bg-primary` (fond du menu) en thème sombre : la sélection ne se voyait pas. Idem pour la liste de modèles | | *BUG-016* | Mobile : le bouton « mode lecture » flottant recouvre le bouton d'envoi de l'assistant IA | 🟢 corrigé | P2 | 📱 frontend | IA | `frontend/js/mobile-editor.js`, `frontend/style.css` | Mobile, sans fichier ouvert : ouvrir l'assistant puis constater que `📖` masque `✈️` | `updateReadingButtonVisibility()` n'affiche `#me-reading-btn` que si `state.currentPath` est défini **et** que le panneau assistant est fermé ; resynchronisé à chaque mutation de `#content-area` et sur `bookslm:opened`/`bookslm:closed` ; règle `.me-reading-btn[hidden]{display:none}` | Le bouton flottant (z-index 890) passait au-dessus du panneau plein écran mobile (z-index 100). Tests : `tests/frontend/mobile-editor.test.mjs` (+2) | | *BUG-017* | Accueil mobile : les tuiles de « Favoris » et « Récents » ne s'affichent pas correctement en largeur (l'onglet « Partagés » s'affiche correctement) | 🟢 corrigé | P2 | 📱 frontend | IA | `frontend/style.css` | Mobile : page d'accueil → onglets Favoris / Récents | `frontend/style.css` : `min-width:0` + `overflow:hidden` sur `.dashboard-card` (et header/footer) → la grille 1 colonne ne déborde plus | Cause : titre/chemin `nowrap` forçaient la largeur mini de la carte | | *BUG-018* | Éditeur mobile : mise en page inadaptée — boutons « Annuler » / « Sauvegarder » inaccessibles et barre d'outils IA non optimisée | 🟢 corrigé | P1 | 📱 frontend | IA | `frontend/style.css` | Mobile : ouvrir un fichier → barre d'outils de l'éditeur puis panneau assistant | `frontend/style.css` : en-tête mobile compact (marque/séparateur/espaceur/présence masqués, champs compressibles, libellé « Saved » masqué) + cibles tactiles `.ai-toolbar-btn` agrandies | Les 3 boutons d'action restent accessibles ; voir aussi #83 pour le ruban | | *BUG-019* | Éditeur mobile : la barre d'outils du bas n'est pas toujours visible au-dessus du clavier quand le curseur n'est pas en bas de l'éditeur | 🟢 corrigé | P1 | 📱 frontend | IA | `frontend/js/mobile-editor.js`, `frontend/style.css` | Mobile : ouvrir un fichier, placer le curseur au milieu du document, afficher le clavier | `initKeyboardAnchor()` utilise `window.visualViewport` (`--kb-offset`) pour ancrer le ruban au-dessus du clavier ; `padding-bottom` sur `.cm-scroller` | Fonctionne quel que soit le défilement / la position du curseur | | *BUG-020* | Éditeur mobile : deux barres de défilement verticales à droite de la page en mode édition | 🟢 corrigé | P3 | 📱 frontend | IA | `frontend/style.css` | Mobile : ouvrir l'éditeur et observer le bord droit | `frontend/style.css` : `.editor-body` en `overflow:hidden` + colonne flex ; seul `.cm-scroller` défile | Une seule barre de défilement | | *BUG-021* | [🔴 CRITIQUE] XSS stocké via le rendu markdown (`escape=False`) | 🟢 corrigé | P0 | 🔐 sécurité | IA | `backend/services/sanitizer.py`, `backend/main.py` | Ouvrir une note contenant `` | `backend/services/sanitizer.py` (whitelist stdlib) appliqué après `_add_heading_ids` dans `_render_markdown` ; tests `tests/test_security_hardening.py` | HTML brut d'un vault injecté dans le DOM → JS dans le navigateur de chaque utilisateur. DOMPurify client non ajouté (défense serveur suffisante) | | *BUG-022* | [🔴 CRITIQUE] XSS stocké sur la page publique `/s/{token}` (title + frontmatter non échappés) | 🟢 corrigé | P0 | 🔐 sécurité | IA | `backend/main.py` | Partager une note dont le frontmatter contient un `title` avec `