62 KiB
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.
🐛 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-16
⚙️ 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. 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)
flowchart LR
A[🔴 ouvert<br/>Découverte d'un bug] --> B[🟠 en cours<br/>IA travaille dessus]
B --> C[🟢 corrigé<br/>Correctif écrit + tests OK]
C --> D[✅ vérifié<br/>Re-validation par l'utilisateur]
C --> E[⚪ abandonné<br/>Refus / hors périmètre]
D --> F[✅ Fermé : lignée conservée en historique]
- Un bug est signalé → posé en
🔴 ouvertpar l'utilisateur ou l'IA (à l'issue d'une investigation ou d'une session de test). - L'IA prend en charge → passe le statut en
🟠 en coursavant de commencer à coder, et crée une entrée dans 🛠️ Journal des interventions AI. - La correction est terminée → l'IA exécute les tests pertinents, passe le statut en
🟢 corrigéet remplit la colonne « Correctif / Commit ». - 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. - 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 à la place.
3. Ordre de lecture pour un agent IA
Avant de corriger quoi que ce soit, un agent IA doit :
- Lire ce fichier en entier (en particulier ce guide et le registre).
- Identifier les rangées de statut
🔴 ouvertavec un scopeIAet une priorité élevée qui lui sont attribuées (colonne « Assigné »). - Poser le statut
🟠 en courssur la rangée choisie avant toute modification de code. - Corriger en respectant la checklist de vérification et les conventions du projet.
- Revenir mettre le statut à
🟢 corrigé, documenter le correctif, et journaliser son passage. - 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-###ouTODO-###(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 <img src=x onerror=alert(1)> |
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 <script> |
backend/main.py : html.escape() sur title/frontmatter + JSON échappé (\u003c) pour le bloc <script> et a.download |
a.download="{title}.md" se trouvait dans un bloc script → breakout JS. Test d'intégration dans tests/test_security_hardening.py |
| BUG-023 | [🔴 CRITIQUE] Brute-force MFA sans rate-limit ni verrouillage | 🟢 corrigé | P0 | 🔐 sécurité | IA | backend/auth/router.py |
POST /api/auth/mfa/totp/verify en boucle avec des codes aléatoires |
_enforce_mfa_rate_limit + _record_mfa_failure sur totp/verify, recovery, webauthn/verify (IP + compte + lockout) |
TOTP 6 chiffres brute-forceable ; le login était protégé, pas le second facteur |
| BUG-024 | [🔴 CRITIQUE] Traversal inter-vaults : startswith() sans séparateur dans resolve_safe_path |
🟢 corrigé | P0 | 🔐 sécurité | IA | backend/services/paths.py |
Avec les vaults vault et vault-evil, lire un fichier de vault-evil via vault |
_is_within() par segments (relative_to + repli casse-insensible) ; tests tests/test_security_hardening.py::TestPathIsolation |
Rupture d'isolation entre vaults (lecture / écriture / suppression) |
| BUG-025 | [🟡 IMPORTANT] ReDoS : regex utilisateur appliquée au contenu en masse | 🟢 corrigé | P1 | 🔐 sécurité | IA | backend/services/regex_safety.py, backend/search.py, backend/services/mutations.py |
Recherche avancée avec ^(a+)+$ sur un gros document |
Validation de pattern (longueur ≤500, rejet quantificateurs imbriqués/backrefs), contenu tronqué (200k) et matchs plafonnés ; 400 sur pattern refusé | Le dry-run replace_in_files déclenchait la charge ; la validation est faite avant compilation |
| BUG-026 | [🟡 IMPORTANT] Webhooks : SSRF (URL non validée) + secret en clair | 🟢 corrigé | P1 | 🔐 sécurité | IA | backend/webhooks.py, data/webhook_secrets.json |
Configurer une URL vers http://169.254.169.254 puis déclencher le webhook |
Validation d'URL (HTTPS par défaut, IP privées/boucle bloquées), résolution DNS au dispatch, redirections non suivies ; secret dans webhook_secrets.json (0600) ou env OBSIGATE_WEBHOOK_SECRET_<ID> |
get_webhooks() n'expose plus le secret (has_secret) ; options OBSIGATE_WEBHOOK_ALLOW_HTTP/_ALLOW_PRIVATE |
| BUG-027 | [🟡 IMPORTANT] Sessions : refresh non rotatif, access token non révoqué au logout | 🟢 corrigé | P1 | 🔐 sécurité | IA | backend/auth/jwt_handler.py, backend/auth/router.py, backend/auth/middleware.py |
Logout puis réutilisation de l'ancien access token | Rotation du refresh token (create_refresh_token(remember=…)) + révocation de l'ancien JTI ; access token révoqué au logout et vérifié dans le middleware |
JTI persistés dans data/revoked_tokens.json |
| BUG-028 | [🟡 IMPORTANT] Politique de mot de passe incohérente + sessions non invalidées au changement | 🟢 corrigé | P1 | 🔐 sécurité | IA | backend/auth/password.py, backend/auth/router.py, backend/auth/user_store.py, backend/auth/middleware.py |
Créer un utilisateur avec password:"" (admin) ; changer son mot de passe puis réutiliser l'ancien token |
validate_password_strength (8–128) sur création/modif admin/changement ; password_changed_at invalide les jetons émis avant le changement ; change-password réémet une paire |
Tests TestPasswordPolicy + TestTokenInvalidation |
| BUG-029 | [🟡 IMPORTANT] Race read-modify-write sur users.json (perte de mises à jour) |
🟢 corrigé | P1 | ⚙️ backend | IA | backend/auth/user_store.py |
Activer MFA et changer son mot de passe en parallèle | threading.RLock (_users_lock) autour de create_user, update_user, delete_user, record_login_failure |
RLock réentrant car record_login_failure appelle update_user |
| BUG-030 | [🟡 IMPORTANT] Adresse IP jamais consignée dans les audits (_request_ip toujours « unknown ») |
🟢 corrigé | P1 | ⚙️ backend | IA | backend/services/net.py, backend/auth/middleware.py |
Lire le journal d'audit après un save/delete | get_client_ip() (request.client.host, X-Forwarded-For si OBSIGATE_TRUST_PROXY=true) injecté dans current_user["_request_ip"] |
Test TestClientIp |
| BUG-031 | [🟡 IMPORTANT] Rate-limiter en mémoire, mono-process, budget exclusivement IP | 🟢 corrigé | P1 | ⚙️ backend | IA | backend/ratelimit.py, backend/auth/router.py |
Lancer plusieurs workers et observer le partage du compteur | Budget par compte (is_account_rate_limited/record_account_*) en plus de l'IP, intégré login + MFA ; limite mono-process documentée |
Rotation d'IP neutralisée ; stockage partagé (Redis) hors périmètre |
| BUG-032 | [🟡 IMPORTANT] Indexation : symlinks suivis (contenu hors vault indexé) + scan initial coûteux | 🟢 corrigé | P1 | ⚙️ backend | IA | backend/indexer.py |
Placer un symlink dans le vault vers un dossier externe puis relancer l'index | _scan_vault réécrit avec os.walk(followlinks=False) + refus des symlinks sortant de la racine ; test TestSymlinkIndexing |
Scan incrémental/index persistant : voir #86 (phase 3) |
| BUG-033 | [🟡 IMPORTANT] Recherche classique et tool IA search_fulltext en O(N) sans inverted index |
🟢 corrigé | P1 | ⚙️ backend | IA | backend/search.py, backend/tools/service.py |
GET /api/search sur un vault de 50 000 fichiers |
search() récupère les candidats via l'inverted index (intersection des termes + expansion de préfixes), repli sur le scan pendant la construction |
search_fulltext en bénéficie automatiquement |
| BUG-034 | [🟡 IMPORTANT] CSP affaiblie ('unsafe-inline' + CDN distants) et token d'accès en sessionStorage |
🟢 corrigé | P1 | 🔐 sécurité | IA | backend/main.py, frontend/js/auth.js, frontend/js/admin.js, frontend/js/sync.js |
Inspecter les en-têtes CSP ; lire sessionStorage en console | Token en mémoire + cookie HttpOnly (plus de sessionStorage) ; CSP durcie (object-src 'none', base-uri, form-action, frame-ancestors). Reste : migration nonce |
'unsafe-inline' conservé tant que les gestionnaires inline n'ont pas été convertis (résidu documenté) |
| BUG-035 | [🔵 MINEUR] secret_redactor : faux positifs sur les hashs hex (git, SHA) |
🔴 ouvert | P2 | ⚙️ backend | IA | backend/secret_redactor.py |
Lire une note contenant un commit git (40 caractères hexadécimaux) | Restreindre le périmètre de détection (contexte clé/token) + whitelist | Contenus mutilés dans les lectures et réponses IA |
| BUG-036 | [🔵 MINEUR] Collab WebSocket : token en query string | 🔴 ouvert | P2 | ⚙️ backend | IA | backend/collab.py |
Observer l'URL du websocket dans le trafic réseau | Passer le token en header / étape d'authentification initiale ; borner la taille des messages | Jeton visible dans les logs/proxys |
| BUG-037 | [🔵 MINEUR] Compte « anonymous » administrateur si auth désactivée | 🔴 ouvert | P2 | 🔐 sécurité | IA | backend/auth/middleware.py |
Démarrer avec l'authentification désactivée | Avertissement explicite au démarrage + refus de déploiement public sans auth | Comportement par conception mais risqué si mal configuré |
| BUG-038 | [🔵 MINEUR] Argon2 à 64 MB par vérification : risque d'épuisement mémoire | 🔴 ouvert | P2 | 🔐 sécurité | IA | backend/auth/password.py:8 |
Lancer de nombreux POST /api/auth/login simultanés |
Recalibrer (~19 MB, t=2, p=1, norme OWASP actuelle) + maintien du rate-limit | DoS mémoire possible sur les petites instances |
| BUG-039 | [🔵 MINEUR] Enumération de comptes : 429 (verrouillé) vs 401 (inconnu) | 🔴 ouvert | P3 | 🔐 sécurité | IA | backend/auth/router.py:120 |
Tenter un login sur un compte verrouillé puis un nom inconnu | Répondre 401 uniforme avec un timing équivalent | Le statut HTTP distingue l'existence d'un compte |
| BUG-040 | [🔵 MINEUR] Extraction PDF intégrale (100 ko) au scan de démarrage | 🔴 ouvert | P2 | ⚙️ backend | IA | backend/indexer.py:465 |
Démarrer sur un vault contenant de nombreux PDF | Analyser les PDF en tâche de fond / à la demande (lazy) | Ralentit fortement le démarrage et le rebuild d'index |
| BUG-041 | [🟡 IMPORTANT] Assistant IA : échec sur un répertoire vide (« Aucun fichier markdown trouvé dans ce dossier ») au lieu de répondre | 🟢 corrigé | P1 | 📱 frontend + ⚙️ backend | IA | backend/bookslm_routes.py, backend/bookslm.py, frontend/js/bookslm.js |
Ouvrir l'assistant sur un dossier vide puis envoyer une question | _resolve_system_prompt dégrade vers le prompt Général + bloc « Dossier vide » (plus de 404) ; contexte applicatif app_context enrichi (documents ouverts, répertoire, recherche, fichiers récents) |
Le 404 bloquait toute la requête. Feature #88, fiche docs/features/ai-app-context.md. Tests : tests/test_bookslm.py (+3), tests/frontend/ai.test.mjs |
| BUG-042 | [🟡 IMPORTANT] Assistant IA : liens de fichiers non fiables (« File not found: ») — pas de règle déterministe nom / dossier / chemin | 🟢 corrigé | P1 | 📱 frontend | IA | frontend/js/bookslm.js |
Cliquer les liens de fichiers/dossiers dans une réponse de l'assistant (noms avec espaces et/ou accents, chemin préfixé par le nom du vault) | _classifyPath distingue name (copie presse-papiers) / dir (révélation arborescence) / file (ouverture) ; _activatePath() résout le chemin contre l'index du vault (exact → suffixe → basename unique) avant d'agir ; espaces + accents pris en charge (classes Unicode \p{L}\p{N}\p{M}, comparaison normalisée NFC, markdown <…>/%20, code inline, mentions brutes confirmées par l'index) ; _splitVaultPrefix retire un préfixe Vault/… et ouvre dans ce vault (_fetchPathsForVault) |
Les liens morts ouvraient un fichier inexistant. Feature #88. Tests : tests/frontend/ai.test.mjs (+11) |
| BUG-043 | [🟡 IMPORTANT] Assistant IA : la liste des fournisseurs de la barre latérale ne suit pas les ajouts/retraits de clés API dans la configuration du projet | 🟢 corrigé | P1 | 📱 frontend | IA | frontend/js/ai.js, frontend/js/bookslm.js, frontend/js/config.js |
Ajouter (ou supprimer) une clé de fournisseur AI dans la configuration puis observer le menu Fournisseur de l'assistant sans recharger la page | Le picker lit /api/ai/status une seule fois, à sa construction, et le panneau de l'assistant est un singleton monté pour toute la session → liste figée. Nouveau refreshAIPickers() (exporté par ai.js) qui reconstruit chaque picker monté dans son emplacement .ai-picker-slot (conservé même sans fournisseur configuré, donc un premier fournisseur s'y monte aussi) ; appelé après saveAIKeys() et deleteAIKey() (config.js) ; une sélection dont le fournisseur n'est plus configuré est purgée de obsigate_ai_picker (retour au défaut + modèle effacé au lieu d'un nom fantôme) |
Il fallait recharger la page pour voir un nouveau fournisseur (ou en voir disparaître un). Feature #82. Tests : tests/frontend/ai.test.mjs (+4) |
| BUG-044 | [🟡 IMPORTANT] Capacités des modèles IA erronées : aucun modèle Mistral n'est détecté « Vision capable » (et des modèles texte sont annoncés « Embeddings ») | 🟢 corrigé | P1 | ⚙️ backend + 🔌 api | IA | backend/model_capabilities.py, backend/provider_capabilities.py, backend/main.py, backend/ai_routes.py |
curl -H "Authorization: Bearer $MISTRAL_API_KEY" https://api.mistral.ai/v1/models puis GET /api/ai/model-capabilities?provider=mistral&model=mistral-small-latest |
Nouveau calque déclaratif (backend/provider_capabilities.py) : les capacités publiées par le fournisseur (Mistral capabilities, OpenRouter architecture) sont lues et mises en cache par GET /api/config/ai-models ; elles priment sur la table statique, qui ne comble plus que les drapeaux non déclarés (et sert de repli hors ligne / sans clé). Table statique corrigée : familles vision Mistral (ministral, magistral, mistral-small, mistral-medium, mistral-vibe-cli, labs-leanstral), mistral-ocr = vision sans chat, défaut fournisseur Mistral sans embeddings. Tests : tests/test_provider_capabilities.py, tests/test_model_capabilities.py::TestMistralFamilies, tests/test_ai_models.py::TestDeclaredCapabilities |
Le panneau de modèle par défaut et la bulle ⓘ n'affichaient aucun Mistral vision alors que l'API en déclare 28 ; seul le motif pixtral (retiré de l'API) matchait. Effet secondaire corrigé : mistral-large-latest / codestral-latest étaient annoncés « Embeddings » (défaut fournisseur). La porte vision de l'assistant (bookslm_routes.py) accepte désormais les images avec un modèle Mistral vision |
| BUG-045 | [🟡 IMPORTANT] Éditeur « Editer » : deux barres de défilement superposées sur les documents longs | 🟢 corrigé | P1 | 📱 frontend | Éditeur | frontend/style.css |
Ouvrir un fichier long (ex. IT/Docker Guide.md), cliquer Editer, mesurer #editor-body et .cm-scroller |
.editor-body-cm gardait overflow:auto et un .cm-editor{height:100%} sous la rangée barre d'outils IA → le corps (toolbar+éditeur) ET le scroller CodeMirror débordaient simultanément. L'override global legacy .cm-scroller{min-height:100%;overflow-y:auto!important} aggravait. Passé en flex column : corps overflow:hidden, toolbar flex:0 0 auto, .cm-editor flex:1 1 auto; height:auto, seul le scroller défile ; override legacy retiré. Test : tests/frontend/editor-inline.test.mjs (+1) ; vérifié Playwright sur l'instance de test (un seul conteneur scrollable). |
|
| BUG-046 | [🔴 BLOQUANT] Assistant IA : « Échec de l'action : [object Object] » à l'application d'un ajout de texte au document courant | 🟢 corrigé | P0 | 📱 frontend + ⚙️ backend | IA | frontend/js/bookslm.js, backend/bookslm.py, backend/bookslm_routes.py |
Mode agent : demander d'ajouter du texte au document ouvert puis cliquer « Appliquer » | Trois causes : (1) continuation de confirmation avec payload:null → second « Appliquer » sans message → 422 ; (2) new Error(detail) sur un detail tableau d'objets FastAPI → « [object Object] » ; (3) prompt documents/directory sans nom de vault → le modèle inventait "vault":"test" → échec silencieux de l'outil. Nouveau _responseError() (aplatit tableau/objet), payload porté à la continuation, bloc « Ces documents appartiennent au vault « X » » + consigne outils d'écriture (build_system_prompt(vault_name=...)). SW_VERSION v20. Tests : tests/test_bookslm.py::test_vault_name_guidance, tests/frontend/ai.test.mjs (+2). |
|
| BUG-047 | [🔴 BLOQUANT] La version affichée par l'application ne suit pas les livraisons : 66 commits livrés depuis v2.2.1 et l'UI/API restent bloquées sur 2.2.1 (et les numéros codés en dur divergent : package.json 1.0.0, desktop Tauri 2.0.0, Dockerfile 2.2.1, README 1.7.0) |
🟢 corrigé | P0 | ⚙️ build + 📄 docs | IA | VERSION (nouveau), scripts/bump_version.py (nouveau), .githooks/prepare-commit-msg + .githooks/post-commit (nouveaux), scripts/install-hooks.sh (nouveau), backend/version.py, Dockerfile, docker-compose.yml, build.sh, .gitea/workflows/ci.yml, desktop/build.rs, tests/test_version.py (nouveau) |
git tag -l | tail -1 puis python scripts/bump_version.py --print-version ; curl -s http://localhost:2020/api/health | jq .version |
Le numéro provenait du dernier tag git et aucun tag n'était créé aux livraisons (bump_version.sh jamais appelé) → version figée, plus quatre numéros codés en dur ailleurs. Corrigé : VERSION (racine) = source unique de vérité, incrémentée automatiquement à chaque commit par le hook versionné prepare-commit-msg (SemVer : !:/BREAKING CHANGE → MAJEUR, feat → MINEUR, sinon CORRECTIF), tag vX.Y.Z créé par post-commit et publié au push (push.followTags) ; bump_version.py resynchronise package.json, desktop Tauri, ROADMAP, READMEs et fait la rotation du CHANGELOG dans le même commit ; backend, image Docker (COPY VERSION) et desktop lisent ce fichier. Contournement ponctuel : SKIP_VERSION_BUMP=1. |
Garde-fou : tests/test_version.py::TestRepoVersionAlignment échoue dès qu'un dérivé diverge de VERSION. Vérifié : pytest complet vert, ruff/mypy 0, /api/health → 2.3.0 sur l'instance de test. |
TODOs techniques (améliorations / nouvelles tâches)
| # | Titre | Statut | Priorité | Scope | Assigné | Zone (fichier) | Cmd de repro | Correctif / Commit | Notes |
|---|---|---|---|---|---|---|---|---|---|
| (exemple) TODO-002 | Rendre l'index inversé incrémental (40k+ fichiers) | 🔴 ouvert | P1 | ⚙️ backend | IA | backend/indexer.py, backend/search.py |
Recherche sur très gros vault | — | Exemple à remplacer. Cf. plan.md |
| (À remplir) |
🛠️ Journal des interventions AI
Chaque passage d'un agent IA qui modifie du code ou l'état du registre est journalisé. Règle : l'IA ajoute une ligne à la fin de ce tableau à chaque session de correction. Colonnes :
Date(YYYY-MM-DD) ·ID(s) traité(s)·Action·Fichiers modifiés·Résumé·Statut après.
| Date | ID(s) traité(s) | Action | Fichiers modifiés | Résumé | Statut après |
|---|---|---|---|---|---|
| (exemple) 2026-06-15 | BUG-001 | Correction | frontend/app.js |
Réécriture de renderFile() pour préserver le DOM dashboard |
🟢 corrigé (en attente vérif) |
| 2026-09-09 | BUG-001, BUG-002 | Correction | backend/main.py, frontend/excalidraw-editor.html, tests/test_pdf_stream.py |
BUG-001: Content-Disposition RFC 5987 (nom PDF accentué ne casse plus l'en-tête → plus de 500). BUG-002: suppression alias esm.sh (408 jotai) + React 19 cohérent + prop excalidrawAPI → Loading masqué, save OK. Vérifié: 534 tests backend verts + E2E navigateur. |
🟢 corrigé (en attente vérif utilisateur) |
| 2026-09-11 | BUG-003, BUG-004 | Correction | backend/{main,indexer,export,pdf_reader,bookslm_routes}.py, backend/auth/router.py, .gitea/workflows/ci.yml, README.md, README.fr.md |
BUG-003: 33 erreurs mypy corrigées (annotations, gardes None, import PROVIDERS manquant → bug latent) + étape CI mypy rendue bloquante. BUG-004: lien README.md → docs/CONTRIBUTING.md. Vérifié: mypy 0 erreur, ruff OK, pytest 728 passed, frontend OK. |
🟢 corrigé (en attente vérif utilisateur) |
| 2026-09-11 | BUG-005 | Correction | frontend/sw.js, frontend/index.html, frontend/js/sync.js, backend/main.py, .gitea/workflows/ci.yml, tests/frontend/sw.test.mjs, tests/test_api_main.py |
BUG-005: chargement mobile incomplet via Cloudflare. SW réécrit en network-first pour HTML/JS/CSS + caches versionnés ; précache corrigé (/static/js/…) ; kill-switch de session remplacé par une migration localStorage ponctuelle ; en-têtes /static passés de immutable 1 an à no-cache ; index.html/manifest/SPA en no-cache ; reload unique sur controllerchange. Vérifié: 754 tests backend, frontend 9+28+8 OK, ruff/mypy OK. |
🟢 corrigé (en attente vérif utilisateur) |
| 2026-09-11 | BUG-006 | Correction sécurité | .env.example |
Clé API DeepSeek réelle exposée dans .env.example → remplacée par un placeholder. Rotation de la clé à faire côté DeepSeek (présente dans l'historique Git). |
🟢 corrigé (rotation à confirmer par l'utilisateur) |
| 2026-09-12 | BUG-007, BUG-008, #82 | Correction + refonte UI | frontend/js/bookslm.js, frontend/js/ai.js, frontend/style.css, frontend/locales/{fr,en}.json, backend/bookslm_routes.py, tests/frontend/ai.test.mjs, tests/test_bookslm.py |
BUG-007 : motifs Unicode + recherche NFD insensible aux accents + navigation ↑/↓ fiabilisée (jeton de séquence, scrollIntoView). BUG-008 : contexte ad-hoc @ pris en compte en mode Général (backend) + repli vault=all. #82 : section « Fournisseur & modèle » compacte (recherche modèle + bulle capacités ℹ️ au survol/clic/appui long). Vérifié : tests frontend 41/41, tests/test_bookslm.py 47 passed. |
🟢 corrigé (en attente vérif utilisateur) |
| 2026-09-12 | BUG-009 | Correction | frontend/js/ai.js, frontend/js/i18n.js, frontend/sw.js, frontend/style.css, frontend/locales/{fr,en}.json, tests/frontend/ai.test.mjs |
BUG-009 : locale non content-hashée servie depuis le cache HTTP → clés i18n brutes ; passage en cache:'no-store' + SW_VERSION v4. Liste de modèles corrompue (OpenRouter, styles en cache) → styles critiques en ligne, options display:block, plafond 200 + indicateur. Vérifié : tests frontend 42/42. |
🟢 corrigé (en attente vérif utilisateur) |
| 2026-09-12 | BUG-010, BUG-011 | Correction | frontend/js/bookslm.js, frontend/js/ai.js, frontend/style.css, tests/frontend/ai.test.mjs |
BUG-010 : capture du vault des résultats @ + _contextVault() propagé aux requêtes /context//chat (mode Général). BUG-011 : popover modèle aligné à droite, largeur 340 px, noms multi-lignes + title. Vérifié en navigateur (Playwright) et tests frontend 43/43. |
🟢 corrigé (en attente vérif utilisateur) |
| 2026-09-12 | BUG-012, #82 | Correction + UX | frontend/js/bookslm.js, frontend/js/ai.js, frontend/style.css, frontend/sw.js, frontend/locales/{fr,en}.json, tests/frontend/ai.test.mjs |
BUG-012 : le menu @ est toujours rendu et _mentionVault() retombe sur le vault de contexte puis le premier vault disponible. #82 : suppression des intitulés « Fournisseur & modèle » / « Fournisseur : », description du contexte Général déplacée en info-bulle, placeholder retiré, bouton Envoyer en emoji ✈️, indicateur d'activité (envoi/réception/outils/confirmation/succès/échec), SW_VERSION v5. Vérifié Playwright + tests frontend 45/45. |
🟢 corrigé (en attente vérif utilisateur) |
| 2026-09-12 | BUG-013, BUG-014 | Correction | frontend/js/bookslm.js, frontend/index.html, frontend/sw.js, tests/frontend/ai.test.mjs |
BUG-013 : liens fichiers/dossiers via _activeVault() (vault de contexte → sélection → premier vault). BUG-014 : navigation ↑/↓ gérée au niveau du panneau en phase de capture. Purge des caches élargie (SW_VERSION v6, migration obsigate-*). Vérifié : tests frontend 47/47, pytest 930. |
🟢 corrigé (en attente vérif utilisateur) |
| 2026-09-12 | BUG-015, #82 | Correction + UX | frontend/js/bookslm.js, frontend/style.css, tests/frontend/ai.test.mjs |
BUG-015 : sélection des menus / et @ rendue visible (--bg-hover + barre d'accent à gauche) car --surface2 = --bg-primary. #82 : retrait de l'indice clavier sous la zone de saisie. Vérifié Playwright (contraste #1f2430 vs #0f1117 + barre accent) et tests frontend 47/47. |
🟢 corrigé (en attente vérif utilisateur) |
| 2026-09-12 | #82 | Amélioration perf + UX | backend/services/search.py, backend/main.py, frontend/js/bookslm.js, frontend/js/ui.js, frontend/style.css, tests/test_api_main.py, tests/frontend/ai.test.mjs |
#82 : endpoint GET /api/vault/{vault}/paths + préchargement et filtrage client du menu @ (instantané) ; sélecteurs Fournisseur/Modèle agrandis (0,8 rem / 34 px) ; « 🧠 BooksLM » ajouté au menu contextuel de la racine des vaults. Vérifié : pytest 151 (ciblés) + ruff/mypy, tests frontend 49/49. |
✅ livré (en attente vérif utilisateur) |
| 2026-09-12 | #82 | UX | frontend/js/ai.js, frontend/style.css, tests/frontend/ai.test.mjs |
Sélecteurs Fournisseur/Modèle alignés à droite dans la barre de l'assistant et réordonnés : capacité du modèle (ⓘ) → fournisseur → modèle. Bulle de capacités ouverte vers la droite (left: 0). Test JSDOM de l'ordre. Vérifié : tests frontend 49/49. |
✅ livré (en attente vérif utilisateur) |
| 2026-09-12 | BUG-016 | Correction | frontend/js/mobile-editor.js, frontend/style.css, tests/frontend/mobile-editor.test.mjs, tests/e2e/mobile-editor.spec.js |
BUG-016 : le bouton « mode lecture » n'apparaît que si un fichier est ouvert et que l'assistant est fermé, sinon il recouvrait le bouton d'envoi sur mobile. Vérifié : tests frontend 24/24 (mobile) + suite JSDOM verte. | 🟢 corrigé (en attente vérif utilisateur) |
| 2026-09-13 | BUG-017, BUG-018, BUG-019, BUG-020, #83 | Enregistrement | docs/ISSUES_TODOLIST.md, docs/ROADMAP.md |
Signalements mobile consignés : accueil (tuiles Favoris/Récents), éditeur (boutons Annuler/Sauvegarder inaccessibles + barre IA), visibilité de la barre d'outils au-dessus du clavier, double barre de défilement. Feature #83 ajoutée : barre d'outils d'édition mobile style Obsidian Android. | 🔴 ouvert (à traiter) |
| 2026-09-13 | Revue sécurité statique (BUG-021 → BUG-040, roadmap #84 → #87) | Enregistrement | docs/ISSUES_TODOLIST.md, docs/ROADMAP.md |
Revue statique 2026-09-13 consignée : XSS markdown (escape=False) + page de partage publique, brute-force MFA, traversal par préfixe paths.py, ReDoS, SSRF webhooks, cycle de vie des sessions, politique de mot de passe, races users.json, audits IP, rate-limit, indexation symlinks, recherche O(N), CSP. BUG-021→BUG-040 ouverts au registre ; roadmap #84→#87 (consolidation/sécurité, refonte architecturale, performance, CI/CD). |
🔴 ouvert (à traiter) |
| 2026-09-13 | BUG-021 → BUG-034 (#84 phase 1) | Correction | backend/services/sanitizer.py, backend/services/paths.py, backend/services/net.py, backend/services/regex_safety.py, backend/main.py, backend/search.py, backend/webhooks.py, backend/indexer.py, backend/ratelimit.py, backend/auth/{router,user_store,password,jwt_handler,middleware}.py, frontend/js/{auth,admin,sync}.js, tests/test_security_hardening.py, tests/test_auth_api.py |
Sanitizer XSS serveur (markdown + page de partage), rate-limit/lockout MFA, isolation vaults par segments, caps regex (ReDoS), SSRF webhooks + secrets externalisés, rotation/révocation des jetons, politique de mot de passe + invalidation des sessions, verrous users.json, IP réelle dans les audits, rate-limit par compte, symlinks d'index ignorés, recherche simple via inverted index, token en mémoire + cookie HttpOnly + CSP durcie. Vérifié : pytest 961 passed / 6 skipped, ruff 0, mypy 0, tests frontend 36 modules + 9 JSDOM suites verts. |
🟢 corrigé (en attente vérif utilisateur) |
| 2026-09-13 | BUG-017 → BUG-020, #83 | Correction + feature | frontend/js/mobile-editor.js, frontend/style.css, frontend/index.html, frontend/locales/{fr,en}.json, tests/frontend/mobile-editor.test.mjs, tests/e2e/mobile-editor.spec.js |
BUG-017 : cartes de tableau de bord compressibles (min-width:0) ; BUG-018 : en-tête éditeur mobile compact + barre IA tactile ; BUG-019 : ancrage du ruban via visualViewport ; BUG-020 : suppression de la double scrollbar. #83 : ruban d'édition horizontal défilable style Obsidian (commandes étendues + personnalisation ⚙ persistée). Vérifié : tests frontend 35/35 (mobile) + 50/50 (IA) + 9 JSDOM suites. |
🟢 corrigé (en attente vérif utilisateur) |
| 2026-09-14 | BUG-042 (complément) | Correction | frontend/js/bookslm.js, tests/frontend/ai.test.mjs, docs/features/ai-app-context.md |
Prise en charge des espaces dans les noms de fichiers et chemins des liens de l'assistant : cibles markdown avec espaces / <…> / %20 décodés, code inline reconnu (_looksLikePath élargi, rejette toujours les extraits de code), mentions brutes liées uniquement si présentes dans l'index du vault (_linkifySpacePaths + _confirmPathInCache, plus long suffixe aligné sur un mot pour ne pas avaler le mot de prose précédent). Vérifié : tests frontend 57/57 (IA) + 9 suites JSDOM, validate-imports 36 modules, pytest 963 passed / 6 skipped. |
🟢 corrigé (en attente vérif utilisateur) |
| 2026-09-14 | BUG-041, BUG-042, #88 | Correction + feature | frontend/js/bookslm.js, backend/bookslm.py, backend/bookslm_routes.py, frontend/locales/{fr,en}.json, tests/test_bookslm.py, tests/frontend/ai.test.mjs, docs/features/ai-app-context.md |
BUG-041 : contexte de dossier vide → dégradation gracieuse vers le prompt Général + bloc « Dossier vide » (fin du 404). BUG-042 : liens de fichiers déterministes (nom → presse-papiers, dossier → arborescence, chemin → viewer) + résolution du chemin contre l'index du vault avant ouverture. #88 : app_context (documents ouverts, répertoire/vault courants, recherche + résultats affichés) et fichiers récemment modifiés injectés dans le prompt Général. Vérifié : pytest 963 passed / 6 skipped, ruff 0, mypy 0, tests frontend 52/52 + validate-imports 36 modules. |
🟢 corrigé (en attente vérif utilisateur) |
| 2026-09-14 | BUG-042 (accents + préfixe vault) | Correction | frontend/js/bookslm.js, tests/frontend/ai.test.mjs, docs/features/ai-app-context.md |
Caractères accentués : classes de caractères Unicode (\p{L}\p{N}\p{M}) pour PATH_WITH_DIR_RE et PATH_NAME_RE (_looksLikePath), comparaison normalisée NFC via _normKey() → une mention décomposée (e + accent combinant, style macOS) correspond à une entrée d'index précomposée, et les liens sans espace mais accentués sont enfin produits. Préfixe de vault : _splitVaultPrefix() retire un premier segment égal à un vault connu (TestVault/Recettes/Pizza Maison.md) ; _fetchPathsForVault() interroge l'index de ce vault sans écraser le cache du vault actif ; _openFileLink/_revealPath reçoivent le vault cible ; repli « retirer le premier segment » si le préfixe est inconnu. Vérifié : tests frontend 62/62 (IA) + 9 suites JSDOM, validate-imports 36 modules, pytest 963 passed / 6 skipped, et vérification contre l'instance live (données réelles accentuées/espacées : Recettes/Préparation.md, Recettes/Pâtes carbonara.md, Recettes/Pizza Maison.md) — 8/8 contrôles. |
🟢 corrigé (en attente vérif utilisateur) |
| 2026-09-14 | BUG-043, #82 | Correction | frontend/js/ai.js, frontend/js/bookslm.js, frontend/js/config.js, tests/frontend/ai.test.mjs, docs/features/ai-provider-picker.md |
La liste des fournisseurs de la barre latérale de l'assistant suit désormais la configuration du projet : nouveau refreshAIPickers() (ai.js) qui reconstruit chaque picker monté dans son emplacement .ai-picker-slot (host conservé dans la barre de l'assistant, montage par replaceChildren), appelé après saveAIKeys() et deleteAIKey() (config.js) ; slot préservé même sans fournisseur configuré (le premier fournisseur ajouté s'y monte) ; sélection persistée d'un fournisseur non configuré purgée de obsigate_ai_picker (retour au défaut, modèle effacé). Vérifié : tests frontend 66/66 (IA) + 9 suites JSDOM vertes, validate-imports 36 modules, pytest 963 passed / 6 skipped, ruff OK, et vérification en navigateur (Playwright) sur l'instance de test : ajout de nvidia → fournisseur visible sans rechargement, retrait → disparition + sélection réinitialisée ({"provider":null,"model":null}), un seul montage du picker dans son host. CI Gitea verte (lint, test, security, build, e2e). |
|
| 2026-09-15 | BUG-044 | Correction | backend/provider_capabilities.py (nouveau), backend/model_capabilities.py, backend/main.py, backend/ai_routes.py, tests/test_provider_capabilities.py (nouveau), tests/test_model_capabilities.py, tests/test_ai_models.py, docs/features/ai-provider-picker.md, CHANGELOG.md |
BUG-044 : les capacités des modèles IA sont désormais lues chez le fournisseur quand il les publie (Mistral capabilities, OpenRouter architecture, détection par forme du payload), mises en cache par GET /api/config/ai-models (aucune requête supplémentaire) et prioritaires sur la table statique qui ne comble plus que les drapeaux non déclarés (repli hors ligne / sans clé). Table statique corrigée : familles vision Mistral (ministral, magistral, mistral-small, mistral-medium, mistral-vibe-cli, labs-leanstral), mistral-ocr = vision sans chat, défaut fournisseur Mistral sans embeddings (mistral-large / codestral n'étaient plus des « embedders »). Vérifié : diagnostic live avant/après sur l'API Mistral (28 modèles vision déclarés, 0 détectés avant → 28 après, 0 écart dans les deux sens), pytest 1007 passed / 6 skipped, ruff 0 (backend), mypy 0 (68 fichiers), tests frontend (unit 9/9, IA 66/66, validate-imports 37 modules), et vérification sur l'instance de test reconstruite (obsigate-test, http://localhost:2020) via les deux endpoints du panneau : mistral-small/medium-latest, ministral-8b-latest, magistral-small-latest → chat,vision ; mistral-ocr-latest → vision seul ; mistral-large-latest/codestral-latest → chat ; mistral-embed → embeddings seul ; deepseek-chat (fournisseur muet) inchangé. |
🟢 corrigé (en attente vérif utilisateur) |
| 2026-09-16 | BUG-045, BUG-046 | Correction | frontend/style.css, frontend/js/bookslm.js, backend/bookslm.py, backend/bookslm_routes.py, frontend/sw.js, tests/test_bookslm.py, tests/frontend/ai.test.mjs, tests/frontend/editor-inline.test.mjs, CHANGELOG.md |
BUG-045 : fin de la double barre de défilement de l'éditeur « Editer » — #editor-body en flex column overflow:hidden, .cm-editor flex:1 1 auto, seul le scroller CodeMirror défile ; suppression de l'override global .cm-scroller (min-height/overflow-y !important). BUG-046 : « Échec de l'action : [object Object] » à l'application d'un ajout de texte au document courant — (1) _responseError() aplatit le detail 422 (tableau d'objets FastAPI) en message lisible, (2) la continuation de confirmation hérite du payload d'origine (le second « Appliquer » ne POSTe plus sans message), (3) le prompt documents/dossier nomme le vault et enjoint les outils d'écriture d'utiliser ce nom exact (le modèle inventait "vault":"test" → échec silencieux). SW_VERSION v20. Vérifié : pytest 1038 passed / 6 skipped, ruff 0 (backend), tests frontend IA 84/84, editor-inline 25/25, validate-imports 38 modules ; reproduction et validation Playwright sur l'instance de test (un seul conteneur scrollable après correctif). |
🟢 corrigé (en attente vérif utilisateur) |
| 2026-09-16 | #93 (complément) | Correction | frontend/js/editor-inline.js, frontend/js/utils.js, frontend/js/viewer.js, frontend/js/ui.js, frontend/js/pane-manager.js, frontend/js/sync.js, tests/frontend/editor-inline.test.mjs, docs/features/editeur-inline.md, docs/CONTRIBUTING.md, CHANGELOG.md |
Garde-fous de session d'édition inline (#93) : l'activation d'un onglet (TabManager.activate, PaneTabManager.activate) appelle detachInlineEditor() avant de vider la zone de contenu, et l'événement SSE index_updated sur le fichier affiché recharge le tampon de l'éditeur (reloadExternalWrite()) au lieu de re-rendre la vue lecture — ces deux chemins détruisaient la session CodeMirror/Forge en cours. Nouveau helper queryEditor() (l'en-tête/pied/marque voyagent avec le conteneur en mode inline, modal.querySelector() les manquait) ; closeEditor() remet le conteneur dans la modale avant de restaurer en-tête/pied/marque. docs/CONTRIBUTING.md documente scripts/install-hooks.sh (hooks de versionnage) dans la mise en place d'un clone. Vérifié : validate-imports 38 modules, unit 9/9, suites JSDOM (editor-inline 25/25, pane-manager, mobile-editor 35/35, toolbar-order, ai 84/84, sw 8/8, collab 10/10, semantic-search 4/4, desktop 23, plugins 21, excalidraw-viewer) toutes vertes, pytest 1082 passed / 6 skipped. |
🟢 corrigé (en attente vérif utilisateur) |
| 2026-09-16 | BUG-047 | Correction + build | VERSION (nouveau), scripts/bump_version.py (nouveau), .githooks/prepare-commit-msg + .githooks/post-commit (nouveaux), scripts/install-hooks.sh (nouveau), scripts/bump_version.sh, backend/version.py, Dockerfile, docker-compose.yml, build.sh, .gitea/workflows/ci.yml, desktop/build.rs, package.json, README.md, README.fr.md, docs/ROADMAP.md, docs/DELIVERY_WORKFLOW.md, docs/DEVELOPMENT_AND_RELEASES.md, tests/test_version.py (nouveau) |
Version alignée de bout en bout. Cause : la version affichée venait du dernier tag git et aucun tag n'était créé aux livraisons → 66 commits livrés mais UI/API figées sur 2.2.1 ; quatre autres numéros codés en dur dérivaient (package.json 1.0.0, desktop 2.0.0, Dockerfile 2.2.1, README 1.7.0). Nouveau modèle : VERSION (racine) = source unique de vérité (MAJEUR.MINEUR.CORRECTIF), incrémentée automatiquement au commit par le hook versionné prepare-commit-msg (SemVer depuis le message : !:/BREAKING CHANGE → MAJEUR, feat → MINEUR, sinon CORRECTIF ; aucun bump pour merge/revert/chore(release)/amend) qui resynchronise package.json, desktop Tauri, ROADMAP, READMEs et publie la section [Unreleased] du CHANGELOG en [X.Y.Z] — date ; post-commit rattache ces fichiers au commit qui vient d'être créé (amend immédiat, le commit n'étant pas encore poussé) puis crée le tag vX.Y.Z, publié au push (push.followTags). bump_version.py (avec --dry-run, --set, --major|--minor|--patch, --print-version) reste utilisable à la main ; scripts/install-hooks.sh installe les hooks. Côté build : Docker COPY VERSION (plus d'ARG VERSION ni d'env compose codés en dur), build.sh et CI lisent VERSION, desktop/build.rs aussi. Vérifié : tests/test_version.py (46 tests, dont le garde-fou TestRepoVersionAlignment : VERSION ↔ package.json ↔ desktop ↔ CHANGELOG ↔ ROADMAP ↔ READMEs ↔ pipeline sans numéro codé en dur), pytest complet, ruff/mypy 0, /api/health → 2.3.0 sur l'instance de test reconstruite. |
🟢 corrigé (en attente vérif utilisateur) |
| 2026-09-16 | #94, #95, #96, #97 | Feature | backend/ai_history.py (nouveau), backend/bookslm_routes.py, frontend/js/bookslm.js, frontend/js/config.js, frontend/index.html, frontend/style.css, frontend/locales/{fr,en}.json, tests/test_bookslm.py, tests/frontend/ai.test.mjs, docs/features/ai-assistant-history.md (nouvelle), docs/ROADMAP.md, CHANGELOG.md | Assistant IA : #95 historique permanent (backend data/ai_history/{user}.json, cap 200, endpoints CRUD /api/ai/bookslm/history[…] résumés/full, sync frontend debounced 600 ms + repli localStorage + migration des clés legacy bookslm-sessions-*/bookslm-history-*, événement bookslm:history-updated) ; #96 onglet sidebar #sidebar-tab-ai (messages-square) + panneau #sidebar-panel-ai (liste chronologique, ouverture via openWithSession) ; #97 bouton « + » remplaçant « Attach an image » + panneau modulaire .bookslm-ext-menu (registre _extensions : Fichiers, Image, Contextes, Skills, Deep Research = mode agent + prompt, Recherche web & Canva en « Bientôt ») ; #94 bouton d'envoi circulaire + icône Lucide arrow-up. Vérifié : pytest 1088 passed / 6 skipped, ruff 0, mypy 0 (71 fichiers), tests frontend IA 84/84, unit 9/9, validate-imports 38 modules. | 🟢 corrigé (en attente vérif utilisateur) |
| 2026-09-16 | BUG-048, #98 | Correction + feature | frontend/js/bookslm.js, frontend/js/config.js, frontend/js/sidebar.js, frontend/style.css, frontend/locales/{fr,en}.json, .gitea/workflows/ci.yml, tests/frontend/ai.test.mjs, tests/frontend/ai-sidebar.test.mjs (nouveau), docs/features/ai-assistant-history.md, docs/ROADMAP.md, CHANGELOG.md | BUG-048 : les entrées « Contextes » et « Skills » du menu « + » ouvraient bien leur menu (@ / /), mais le clic remontait au gestionnaire du panneau qui annulait le rendu asynchrone → menu jamais affiché ; correction par e.stopPropagation() sur les entrées du panneau .bookslm-ext-menu. #98 : la barre de filtrage de la sidebar agit désormais sur l'onglet « Historique IA » — filterAIHistory() (config.js) filtre par titre, aperçu, répertoire, contexte ou libellé de mode, insensible casse/accents (_aiNorm), cache sessions _aiSessionsCache, message « aucune correspondance » (bookslm.history_no_match) dans la liste et placeholder dédié (sidebar.filter_ai) ; initSidebarFilter (sidebar.js) route saisie/touche casse/bouton « × » vers filterAIHistory quand l'onglet IA est actif ; chaque entrée du panneau « + » porte l'icône Lucide plus. Vérifié : tests frontend IA 87/87 (+3), nouvelle suite ai-sidebar 6/6, unit 9/9, 9 suites JSDOM vertes, validate-imports 38 modules, pytest / ruff / mypy inchangés (aucune modification backend). | 🟢 corrigé (en attente vérif utilisateur) — CI Gitea verte (lint, test, security, build, e2e) pour v2.5.0 (run #1511) |
| 2026-09-16 | BUG-049, #99, #100 | Correction + feature | frontend/style.css, frontend/js/config.js, frontend/js/viewer.js, frontend/js/sidebar.js, frontend/js/bookslm.js, frontend/locales/{fr,en}.json, .gitea/workflows/ci.yml, tests/frontend/ai.test.mjs, tests/frontend/sidebar-filters.test.mjs (nouveau), docs/features/sidebar-filters.md (nouvelle), docs/features/ai-assistant-history.md, docs/ROADMAP.md, CHANGELOG.md | BUG-049 : icône du bouton « + » de l'assistant invisible — la règle générique .bookslm-input-area button (spécificité supérieure) imposait padding: 8px 16px sur un bouton width: 32px ⇒ largeur de contenu nulle ⇒ SVG width: 0px ; sélecteur porté à .bookslm-input-area button.bookslm-btn-plus (+ :hover), vérifié en navigateur (Playwright : SVG 0 px → 18 px). #99 : la barre de filtrage de la sidebar agit désormais sur les vues Récents (filterRecentFiles, titre/chemin/vault/aperçu/tags) et Sauvegardes (filterSavedSearches, cumulable avec les pills type), insensible casse/accents (_sidebarNorm/_savedNorm), message d'absence de résultat (sidebar.no_results) et placeholders dédiés (sidebar.filter_recent, sidebar.filter_saved) ; initSidebarFilter refactoré en routeFilter/routeClear couvrant les 5 onglets. #100 : « Deep Research » ajoute une pastille .bookslm-chip-deep-research (au lieu d'injecter la directive dans le composeur), active le mode Agent et injecte la directive au moment de l'envoi. Vérifié : tests frontend IA 88/88 (+1), sidebar-filters 8/8 (nouveau), ai-sidebar 6/6, unit 9/9, 9 suites JSDOM vertes, validate-imports 38 modules, vérification navigateur du bouton « + » ; backend inchangé (pytest / ruff / mypy valides). | 🟢 corrigé (en attente vérif utilisateur) |
📜 Historique des bugs résolus
Les rangées
✅ vérifié(ou⚪ abandonné) sont déplacées ici régulièrement pour garder le registre actuel court et lisible. Conservez l'ID d'origine pour la traçabilité.
| # | Titre | Date résolution | Résolu par | Correctif / Commit | Notes |
|---|---|---|---|---|---|
| (aucun pour l'instant) |
🧪 Procédure de vérification d'une correction (pour l'IA)
Quand une correction est écrite, l'agent IA doit avant de passer en 🟢 corrigé :
- Exécuter les tests du projet :
- Backend :
python -m pytest tests/ -v(oupytest) - S'il existe des tests ciblés sur la zone modifiée, les lancer en priorité.
- Backend :
- Vérifier le lancement du module concerné (import sans erreur, endpoint répond).
- Relire sa propre diff pour éviter les régressions.
- Mettre la colonne « Correctif / Commit » à jour puis le statut en
🟢 corrigé. - Si un test échoue ou que la correction est incomplète, rester en
🟠 en courset le noter.
✔️ Checklist de traitement d'un bug (rappel pour toute IA)
- Bug identifié avec un ID et repris dans le registre
- Statut posé en
🟠 en coursavant de coder - Correction conforme aux conventions du projet
- Tests exécutés et vert (colonne « Correctif / Commit » renseignée)
- Journal des interventions AI mis à jour
- Statut passé en
🟢 corrigé(pas✅ vérifiésauf autorisation) - (Après validation utilisateur) rangée déplacée dans l'historique
📚 Liens utiles
- Guide de contribution : CONTRIBUTING.md (conventions de code)
- Structure du projet : README.fr.md
- Tests : répertoire
tests/— voir CONTRIBUTING - Analyse/décisions complémentaires : IMPLEMENTATION_PLAN.md, ROADMAP.md