chore: Add autonomous agent infrastructure and cleanup old files

- Disable CLAUDE.md confirmation rules for autonomous agent operation
- Add utility scripts: check_linear_status.py, check_meta_issue.py, move_issues_to_todo.py
- Add works filter specification: prompts/app_spec_works_filter.txt
- Update .linear_project.json with works filter issues
- Remove old/stale scripts and documentation files
- Update search.html template

This commit completes the infrastructure for the autonomous agent that
successfully implemented all 13 works filter issues (LRP-136 to LRP-148).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-04 16:42:42 +01:00
parent fe085c7ebe
commit a73ed2d98e
31 changed files with 1074 additions and 31318 deletions

View File

@@ -0,0 +1,650 @@
<project_specification>
<project_name>Library RAG - Filtrage par œuvres dans la conversation</project_name>
<overview>
Système de filtrage par œuvres pour la page de conversation RAG, permettant aux utilisateurs de sélectionner les œuvres sur lesquelles effectuer la recherche sémantique.
**Objectif :**
Ajouter une section "Filtrer par œuvres" dans la sidebar droite (au-dessus du "Contexte RAG") avec des cases à cocher pour chaque œuvre disponible. Chaque message de la conversation recherchera uniquement dans les œuvres sélectionnées.
**Architecture :**
- Backend : Nouvelle route API + modification de la recherche Weaviate avec filtres
- Frontend : Nouvelle section UI avec checkboxes + JavaScript pour état et persistance
- Persistance : localStorage pour sauvegarder la sélection entre les sessions
**Comportement par défaut :**
- Toutes les œuvres cochées au démarrage
- Section collapsible avec chevron
- Boutons "Tout" / "Aucun" pour sélection rapide
- Badge compteur : "X/Y sélectionnées"
- Responsive mobile : section pliable au-dessus de l'input
**Contraintes :**
- Ne pas modifier l'export Word/PDF existant
- Conserver la structure grid 60%/40% (conversation/sidebar)
- Compatibilité avec les 3 modes de recherche (simple, hiérarchique, summary)
</overview>
<technology_stack>
<backend>
<framework>Flask 3.0+</framework>
<database>Weaviate 1.34.4 + text2vec-transformers (BGE-M3)</database>
<python>Python 3.10+</python>
</backend>
<frontend>
<template_engine>Jinja2</template_engine>
<javascript>Vanilla JavaScript (ES6+)</javascript>
<css>Custom CSS (variables CSS existantes)</css>
<storage>localStorage (Web Storage API)</storage>
</frontend>
</technology_stack>
<implementation_steps>
<feature_1>
<title>Backend - Route API /api/get-works</title>
<description>
Créer une route GET qui retourne la liste de toutes les œuvres disponibles avec métadonnées.
Tasks:
- Ajouter route @app.route("/api/get-works") dans flask_app.py (après ligne ~1380)
- Utiliser get_weaviate_client() context manager
- Query collection "Chunk" pour extraire toutes les œuvres uniques
- Parser propriété nested "work" (work.title, work.author)
- Compter les chunks par œuvre (chunks_count)
- Retourner JSON trié par auteur puis titre
- Gérer les erreurs de connexion Weaviate
- Ajouter logging pour debug
Format de sortie JSON :
[
{
"title": "Ménon",
"author": "Platon",
"chunks_count": 127
},
...
]
</description>
<priority>1</priority>
<category>backend</category>
<test_steps>
1. Démarrer Weaviate : docker compose up -d
2. Démarrer Flask : python flask_app.py
3. Tester route : curl http://localhost:5000/api/get-works
4. Vérifier JSON retourné contient toutes les œuvres
5. Vérifier tri alphabétique par auteur
6. Vérifier chunks_count > 0 pour chaque œuvre
7. Tester avec Weaviate arrêté : vérifier erreur 500 propre
</test_steps>
</feature_1>
<feature_2>
<title>Backend - Modification route /chat/send</title>
<description>
Modifier la route POST /chat/send pour accepter le paramètre selected_works et le passer à la fonction de recherche.
Tasks:
- Localiser route @app.route("/chat/send", methods=["POST"]) (ligne ~1756)
- Ajouter extraction paramètre selected_works du JSON body
- Type : List[str] (liste des titres d'œuvres)
- Valeur par défaut : [] (liste vide = toutes les œuvres)
- Valider que selected_works est bien une liste
- Passer selected_works à la fonction de recherche sémantique
- Logger les œuvres sélectionnées pour debug
- Gérer cas où selected_works = [] (pas de filtre)
Exemple JSON input :
{
"question": "Qu'est-ce que la justice ?",
"provider": "openai",
"model": "gpt-4o",
"limit": 5,
"selected_works": ["Ménon", "La pensée-signe"]
}
</description>
<priority>1</priority>
<category>backend</category>
<test_steps>
1. Modifier /chat/send pour accepter selected_works
2. Tester POST avec selected_works vide : curl -X POST -H "Content-Type: application/json" -d '{"question":"test","provider":"openai","model":"gpt-4o","selected_works":[]}' http://localhost:5000/chat/send
3. Vérifier que la recherche fonctionne sans filtre
4. Tester POST avec selected_works = ["Ménon"]
5. Vérifier que le paramètre est bien reçu (ajouter print/log temporaire)
6. Tester POST avec selected_works invalide (pas une liste)
7. Vérifier gestion d'erreur propre
</test_steps>
</feature_2>
<feature_3>
<title>Backend - Filtre Weaviate par œuvres</title>
<description>
Implémenter la logique de filtrage Weaviate pour rechercher uniquement dans les œuvres sélectionnées.
Tasks:
- Localiser la fonction de recherche sémantique dans /chat/send
- Identifier les requêtes Weaviate (near_text sur Chunk et Summary)
- Ajouter paramètre selected_works à ces fonctions
- Construire filtre Weaviate si selected_works non vide :
- Pour Chunk : Filter.by_property("work").by_property("title").contains_any(selected_works)
- Pour Summary : Idem si Summary a work nested
- Appliquer filtre dans :
- Recherche simple (Chunk.query.near_text)
- Recherche hiérarchique (Summary → Chunk)
- Recherche summary uniquement
- Tester syntaxe filtre Weaviate v4 (contains_any sur nested property)
- Gérer cas où aucun résultat trouvé avec filtre
- Logger requêtes Weaviate pour debug
Note critique : Weaviate v4 syntax pour nested properties
</description>
<priority>1</priority>
<category>backend</category>
<test_steps>
1. Identifier fonction de recherche actuelle dans flask_app.py
2. Ajouter filtre Weaviate pour selected_works
3. Tester recherche sans filtre (selected_works=[])
4. Vérifier résultats de toutes les œuvres
5. Tester recherche avec filtre (selected_works=["Ménon"])
6. Vérifier que SEULS les chunks de Ménon sont retournés
7. Tester avec œuvre inexistante : vérifier 0 résultats
8. Tester mode hiérarchique avec filtre
9. Vérifier que Summary ET Chunk sont filtrés
</test_steps>
</feature_3>
<feature_4>
<title>Frontend - HTML section filtrage œuvres</title>
<description>
Créer la section HTML "Filtrer par œuvres" dans la sidebar droite, au-dessus du "Contexte RAG".
Tasks:
- Ouvrir templates/chat.html
- Localiser ligne ~710 (début de .context-sidebar)
- AVANT la div .context-sidebar, ajouter nouvelle div .works-filter-section
- Structure HTML :
- Header avec titre + badge compteur + bouton collapse
- Content avec boutons "Tout"/"Aucun"
- Div .works-list (remplie dynamiquement par JS)
- IDs pour JavaScript :
- works-filter-section
- works-filter-content
- works-collapse-btn
- works-count-badge
- works-list
- select-all-works
- select-none-works
- Classe sidebar-empty pour état de chargement
- Respecter structure HTML existante (sidebar-header, sidebar-content)
Note : Ne pas modifier la div .context-sidebar existante
</description>
<priority>1</priority>
<category>frontend</category>
<test_steps>
1. Ouvrir http://localhost:5000/chat dans le navigateur
2. Vérifier que nouvelle section apparaît AU-DESSUS du Contexte RAG
3. Vérifier header avec titre "📚 Filtrer par œuvres"
4. Vérifier badge compteur visible
5. Vérifier bouton collapse (chevron ▼)
6. Vérifier boutons "Tout" et "Aucun" présents
7. Vérifier div .works-list vide au démarrage
8. Vérifier que la section Contexte RAG est toujours visible en-dessous
</test_steps>
</feature_4>
<feature_5>
<title>Frontend - CSS pour section filtrage</title>
<description>
Ajouter les styles CSS pour la section de filtrage par œuvres, cohérents avec le design existant.
Tasks:
- Dans templates/chat.html, section &lt;style&gt; (ligne ~6)
- Ajouter styles pour :
- .works-filter-section (structure générale)
- .works-filter-content (max-height 250px, scroll)
- .works-count-badge (badge compteur)
- .works-filter-actions (boutons Tout/Aucun)
- .btn-mini (style boutons)
- .works-list (liste des œuvres)
- .work-item (chaque œuvre)
- .work-checkbox (case à cocher)
- .work-info (titre + auteur)
- .work-title, .work-author (typographie)
- .work-count (badge nombre de passages)
- Utiliser variables CSS existantes :
- --color-accent, --color-accent-alt
- --color-text-main, --color-text-strong
- --font-body, --font-title
- Ajouter hover effects
- Responsive : @media (max-width: 992px) pour mobile
- Cohérence avec .context-sidebar existante
Note : Réutiliser les styles de .context-chunk pour cohérence
</description>
<priority>1</priority>
<category>frontend</category>
<test_steps>
1. Recharger page /chat
2. Vérifier styles appliqués sur section filtrage
3. Vérifier bordures, border-radius cohérents
4. Vérifier couleurs cohérentes avec palette existante
5. Tester hover sur boutons "Tout"/"Aucun"
6. Tester hover sur work-item
7. Vérifier scrollbar sur .works-list si >250px
8. Tester responsive : réduire fenêtre < 992px
9. Vérifier que section est collapsible sur mobile
</test_steps>
</feature_5>
<feature_6>
<title>Frontend - JavaScript état et rendu</title>
<description>
Implémenter la logique JavaScript pour gérer l'état des œuvres sélectionnées et le rendu de la liste.
Tasks:
- Dans templates/chat.html, section &lt;script&gt; (après ligne ~732)
- Déclarer variables globales :
- availableWorks: Array&lt;Work&gt; (liste complète)
- selectedWorks: Array&lt;string&gt; (titres sélectionnés)
- Créer fonction loadAvailableWorks() :
- Fetch GET /api/get-works
- Stocker dans availableWorks
- Initialiser selectedWorks (tous par défaut ou localStorage)
- Appeler renderWorksList()
- Créer fonction renderWorksList() :
- Vider works-list
- Pour chaque work, créer HTML :
- Checkbox (checked si dans selectedWorks)
- work-info (titre + auteur)
- work-count (nombre passages)
- Ajouter event listeners sur checkboxes
- Click sur work-item toggle checkbox
- Créer fonction toggleWorkSelection(title, isSelected)
- Créer fonction updateWorksCount()
- Appeler loadAvailableWorks() au chargement de la page
Note : Utiliser addEventListener, pas d'inline onclick
</description>
<priority>1</priority>
<category>frontend</category>
<test_steps>
1. Ouvrir console navigateur (F12)
2. Recharger page /chat
3. Vérifier que fetch /api/get-works est appelé (Network tab)
4. Vérifier que availableWorks contient les œuvres (console.log)
5. Vérifier que .works-list contient des work-item
6. Cocher/décocher une œuvre
7. Vérifier que selectedWorks est mis à jour (console.log)
8. Vérifier que badge compteur est mis à jour
9. Cliquer sur work-item (pas la checkbox)
10. Vérifier que checkbox est togglee
</test_steps>
</feature_6>
<feature_7>
<title>Frontend - JavaScript persistance localStorage</title>
<description>
Implémenter la sauvegarde automatique de la sélection dans localStorage pour persister entre les sessions.
Tasks:
- Créer fonction saveSelectedWorksToStorage() :
- localStorage.setItem('selectedWorks', JSON.stringify(selectedWorks))
- Appeler saveSelectedWorksToStorage() après chaque modification :
- Dans toggleWorkSelection()
- Dans selectAllWorksBtn click
- Dans selectNoneWorksBtn click
- Modifier loadAvailableWorks() :
- Charger localStorage.getItem('selectedWorks')
- Parser JSON si existe
- Sinon, sélectionner toutes les œuvres par défaut
- Gérer cas où œuvres ont changé :
- Filtrer selectedWorks pour ne garder que celles qui existent
- Mettre à jour localStorage
- Ajouter fonction clearWorksStorage() pour debug (optionnel)
Note : Vérifier que localStorage est disponible (try-catch)
</description>
<priority>2</priority>
<category>frontend</category>
<test_steps>
1. Ouvrir /chat et sélectionner 2 œuvres uniquement
2. Vérifier dans DevTools → Application → Local Storage
3. Vérifier clé 'selectedWorks' contient JSON des 2 œuvres
4. Rafraîchir la page (F5)
5. Vérifier que les 2 œuvres sont toujours cochées
6. Cliquer "Tout" puis rafraîchir
7. Vérifier que toutes les œuvres sont cochées
8. Cliquer "Aucun" puis rafraîchir
9. Vérifier qu'aucune œuvre n'est cochée
10. Tester en navigation privée (pas de localStorage)
</test_steps>
</feature_7>
<feature_8>
<title>Frontend - JavaScript intégration recherche</title>
<description>
Modifier la fonction startRAGSearch() pour envoyer les œuvres sélectionnées au backend lors de la recherche.
Tasks:
- Localiser fonction startRAGSearch(question, provider, model) (ligne ~943)
- Modifier le fetch POST /chat/send :
- Ajouter clé "selected_works" au JSON body
- Valeur : selectedWorks (array global)
- Ajouter validation avant envoi :
- Si selectedWorks.length === 0, afficher warning ?
- Ou laisser passer (recherche sur toutes)
- Tester que le filtre fonctionne :
- Contexte RAG affiché ne contient QUE les œuvres sélectionnées
- Réponse LLM basée uniquement sur ces œuvres
- Ajouter logging console.log pour debug
- Gérer erreur si aucun résultat trouvé avec filtre
Note : Pas besoin de modifier displayContext() si backend filtre correctement
</description>
<priority>1</priority>
<category>frontend</category>
<test_steps>
1. Ouvrir /chat
2. Sélectionner uniquement œuvre "Ménon"
3. Poser question : "Qu'est-ce que la vertu ?"
4. Vérifier dans Network tab : POST /chat/send contient selected_works: ["Ménon"]
5. Vérifier que contexte RAG ne contient QUE des chunks de Ménon
6. Vérifier que réponse LLM mentionne Ménon (pas d'autres œuvres)
7. Sélectionner 2 œuvres : "Ménon" + "La pensée-signe"
8. Poser nouvelle question
9. Vérifier contexte contient ces 2 œuvres uniquement
10. Désélectionner toutes les œuvres et tester
</test_steps>
</feature_8>
<feature_9>
<title>Frontend - Boutons actions et collapse</title>
<description>
Implémenter les boutons "Tout" / "Aucun" et le comportement de collapse de la section.
Tasks:
- Event listener selectAllWorksBtn :
- selectedWorks = availableWorks.map(w =&gt; w.title)
- Appeler renderWorksList()
- Appeler updateWorksCount()
- Appeler saveSelectedWorksToStorage()
- Event listener selectNoneWorksBtn :
- selectedWorks = []
- Appeler renderWorksList()
- Appeler updateWorksCount()
- Appeler saveSelectedWorksToStorage()
- Event listener worksCollapseBtn :
- Toggle display de works-filter-content
- Changer texte chevron (▼ / ▲)
- Changer title tooltip ("Réduire" / "Développer")
- Optionnel : sauvegarder état collapse dans localStorage
- Ajouter transition CSS pour collapse smooth
Note : Réutiliser logique existante de collapseBtn du Contexte RAG
</description>
<priority>2</priority>
<category>frontend</category>
<test_steps>
1. Ouvrir /chat
2. Cliquer bouton "Tout"
3. Vérifier que toutes les œuvres sont cochées
4. Vérifier badge compteur : "X/X sélectionnées"
5. Cliquer bouton "Aucun"
6. Vérifier qu'aucune œuvre n'est cochée
7. Vérifier badge compteur : "0/X sélectionnées"
8. Cliquer chevron collapse
9. Vérifier que .works-filter-content disparaît
10. Vérifier chevron change (▼ → ▲)
11. Recliquer chevron : section réapparaît
</test_steps>
</feature_9>
<feature_10>
<title>Frontend - Responsive mobile</title>
<description>
Adapter l'interface de filtrage pour les écrans mobiles (&lt; 992px).
Tasks:
- Ajouter @media (max-width: 992px) dans CSS
- Sur mobile :
- .works-filter-section : order: -2 (avant contexte RAG)
- max-height: 200px
- .works-filter-content : max-height: 150px
- Section collapsée par défaut
- Badge compteur plus visible
- Tester sur petits écrans :
- iPhone (375px)
- iPad (768px)
- Desktop réduit (900px)
- Vérifier que section ne prend pas trop de place
- Vérifier scroll horizontal n'apparaît pas
- Vérifier touch events fonctionnent (pas que click)
Note : La structure grid actuelle passe déjà en 1 colonne sur mobile
</description>
<priority>2</priority>
<category>frontend</category>
<test_steps>
1. Ouvrir DevTools → Toggle device toolbar (Ctrl+Shift+M)
2. Sélectionner iPhone 12 Pro (390x844)
3. Vérifier que section filtrage apparaît
4. Vérifier hauteur limitée à 200px
5. Vérifier scroll fonctionne si liste longue
6. Tester checkbox touch/click
7. Tester boutons "Tout"/"Aucun"
8. Tester collapse fonctionne
9. Passer en iPad (768px)
10. Vérifier layout correct
11. Revenir desktop (>992px) : vérifier layout normal
</test_steps>
</feature_10>
<feature_11>
<title>Testing - Tests backend routes</title>
<description>
Créer tests unitaires pour les nouvelles routes backend et la logique de filtrage.
Tasks:
- Créer tests/test_works_filter.py
- Tester route /api/get-works :
- Mock get_weaviate_client()
- Mock collection Chunk avec données test
- Vérifier JSON retourné correct
- Vérifier tri par auteur
- Vérifier chunks_count calculé
- Tester erreur Weaviate
- Tester /chat/send avec selected_works :
- Mock fonction de recherche
- Vérifier paramètre passé correctement
- Tester avec selected_works vide
- Tester avec selected_works = ["Ménon"]
- Tester logique filtre Weaviate :
- Mock Weaviate query
- Vérifier filtre contains_any appliqué
- Vérifier résultats filtrés
- Utiliser pytest et pytest-mock
- Vérifier couverture >80%
Note : Ne pas faire d'appels API réels en tests
</description>
<priority>3</priority>
<category>testing</category>
<test_steps>
1. Installer pytest : pip install pytest pytest-mock
2. Créer tests/test_works_filter.py
3. Exécuter : pytest tests/test_works_filter.py -v
4. Vérifier tous les tests passent
5. Exécuter avec coverage : pytest --cov=flask_app tests/test_works_filter.py
6. Vérifier couverture >80% pour routes concernées
7. Tester avec Weaviate mock : aucun appel réseau réel
8. Vérifier temps d'exécution <5s
</test_steps>
</feature_11>
<feature_12>
<title>Testing - Tests frontend JavaScript</title>
<description>
Tests manuels de la logique JavaScript dans le navigateur.
Tasks:
- Tester loadAvailableWorks() :
- Console : vérifier availableWorks peuplé
- Vérifier selectedWorks initialisé
- Tester renderWorksList() :
- Vérifier work-items créés dynamiquement
- Vérifier checkboxes cochées selon selectedWorks
- Tester toggleWorkSelection() :
- Cocher/décocher plusieurs œuvres
- Vérifier selectedWorks mis à jour
- Vérifier badge compteur synchronisé
- Tester localStorage :
- Modifier sélection
- Rafraîchir page
- Vérifier persistance
- Tester intégration recherche :
- Sélectionner œuvre
- Envoyer question
- Vérifier filtre appliqué
- Tester boutons Tout/Aucun
- Tester collapse
- Tester responsive
Note : Tests manuels car pas de framework test JS configuré
</description>
<priority>3</priority>
<category>testing</category>
<test_steps>
1. Ouvrir /chat avec DevTools (F12)
2. Console : taper availableWorks puis Enter
3. Vérifier array d'œuvres affiché
4. Console : taper selectedWorks puis Enter
5. Vérifier array de titres sélectionnés
6. Cocher/décocher œuvre
7. Retaper selectedWorks : vérifier mise à jour
8. Application tab → Local Storage → selectedWorks
9. Vérifier JSON synchronisé
10. Envoyer question test
11. Network tab → POST /chat/send → Preview
12. Vérifier selected_works dans payload
</test_steps>
</feature_12>
<feature_13>
<title>Documentation - Guide utilisateur</title>
<description>
Documenter la nouvelle fonctionnalité de filtrage par œuvres.
Tasks:
- Mettre à jour README.md ou créer WORKS_FILTER.md
- Documenter :
- Fonctionnalité de filtrage
- Comportement par défaut (toutes cochées)
- Boutons "Tout" / "Aucun"
- Persistance localStorage
- Impact sur la recherche sémantique
- Cas d'usage recommandés
- Ajouter captures d'écran (optionnel)
- Documenter API /api/get-works
- Documenter modification /chat/send
- Ajouter troubleshooting :
- Que faire si aucune œuvre affichée ?
- Que faire si filtre ne fonctionne pas ?
- Comment réinitialiser la sélection ?
Note : Documentation utilisateur, pas technique
</description>
<priority>3</priority>
<category>documentation</category>
<test_steps>
1. Lire README.md ou WORKS_FILTER.md
2. Vérifier clarté des explications
3. Vérifier exemples concrets fournis
4. Tester instructions étape par étape
5. Vérifier troubleshooting couvre cas courants
6. Vérifier API documentée (endpoints, params, retour)
7. Relire pour fautes orthographe/grammaire
</test_steps>
</feature_13>
</implementation_steps>
<testing_strategy>
<backend_tests>
<structure>
tests/
└── test_works_filter.py
</structure>
<coverage>
- Route /api/get-works (mock Weaviate)
- Route /chat/send avec selected_works (mock recherche)
- Logique filtre Weaviate (mock query)
- Cas limites (liste vide, œuvre inexistante)
</coverage>
</backend_tests>
<frontend_tests>
<manual_testing>
- Tests manuels dans navigateur (Chrome, Firefox)
- Tests console JavaScript
- Tests localStorage DevTools
- Tests Network DevTools
- Tests responsive (DevTools device mode)
</manual_testing>
<no_automated_js_tests>
Pas de framework de test JS (Jest, Mocha) configuré.
Tests manuels suffisants pour cette feature.
</no_automated_js_tests>
</frontend_tests>
</testing_strategy>
<success_criteria>
<functional>
- Route /api/get-works retourne toutes les œuvres avec métadonnées
- Section "Filtrer par œuvres" visible dans sidebar droite
- Checkboxes fonctionnelles pour chaque œuvre
- Sélection persiste entre les sessions (localStorage)
- Recherche filtrée uniquement sur œuvres sélectionnées
- Contexte RAG ne contient QUE les œuvres sélectionnées
- Boutons "Tout" / "Aucun" fonctionnels
- Section collapsible avec chevron
- Badge compteur synchronisé
- Responsive mobile fonctionnel
</functional>
<quality>
- Code backend suit conventions Flask existantes
- Code frontend suit conventions JavaScript existantes
- CSS cohérent avec design existant (variables CSS)
- Pas de console errors JavaScript
- Pas d'erreurs 500 backend
- Tests backend passent (>80% coverage)
</quality>
<performance>
- /api/get-works répond en <500ms
- Rendu liste œuvres <100ms
- Pas de lag lors du check/uncheck
- localStorage lecture/écriture instantanée
</performance>
<ux>
- Comportement par défaut intuitif (toutes cochées)
- Feedback visuel immédiat sur sélection
- Badge compteur toujours à jour
- Pas de confusion avec section Contexte RAG
- Mobile : section accessible et utilisable
</ux>
</success_criteria>
<deployment>
<no_deployment>
Modification de l'application Flask existante.
Pas de déploiement séparé nécessaire.
Redémarrage Flask après modifications :
- Ctrl+C pour arrêter
- python flask_app.py pour redémarrer
</no_deployment>
</deployment>
</project_specification>