Automatise, sur un switch HPE Comware 7, 4 méthodes de capture réseau
(packet-capture local, packet-capture remote/RPCAP, port mirroring
SPAN/ERSPAN, mirroring distant VLAN+VXLAN L2) ainsi que la désinstallation
propre de la configuration poussée. Détail : docs/architecture.md.
- 63 sessions de développement documentées (23/08 → 16/09/2026) —
index :
docs/sessions/index.md - 20/20 demandes numérotées historiques traitées, 0 point numéroté encore ouvert
- Dépendances gérées par
uvdepuis la session 63 (remplacepip+requirements.txt/requirements-dev.txt, supprimés) :pyproject.tomlporte désormais[project](métadonnées + dépendances runtime) et[dependency-groups](devsynchronisé par défaut,quality— ruff/ coverage — sur demande via--group quality), verrouillées dansuv.lock(nouveau fichier, 65 paquets résolus). Toujours aucun[build-system]: le choix « pas de packaging » (voirtests/conftest.py) n'a pas changé, voirdocs/sessions/session-63.mdpour la vérification complète (docuvofficielle citée, projet effectivement non installé dans son propre venv). Niinstall.shnibuild_deb.sh/build_rpm.shne touchent àuv: ils listaient déjà leurs dépendances indépendamment derequirements.txt, donc inchangés — voir « Commandes de qualité » ci-dessous pour les nouvelles commandes de développement. - Suite de tests : 661 passés (632 + 29), 1 échec préexistant sans
rapport (
ip/iproute2absent de ce sandbox), 10 skips (GTK4/PyGObject indisponible) — inchangé numériquement depuis la session 62, revérifié viauv run pytest -qen session 63 uv run --group quality ruff check .(dépôt entier, pas seulement les fichiers modifiés) : 0 erreur — 53 erreurs pré-existantes (BLE001/C408/RUF100/PLW1510, accumulées sur plusieurs sessions) corrigées en session 58, détail :docs/sessions/session-58.mduv run --group quality ruff format --check .: 0 fichier à reformater (56 conformes) — les 16 fichiers non conformes constatés en session 58 traités en session 61 (neutralité prouvée par comparaison d'AST avant/après sur les 44 fichiers Python), détail :docs/sessions/session-61.md. Plus aucune detteruffen attente dans le dépôt : à garder conforme au fil de l'eau.⚠️ Le reformatage de la session 61 décale les numéros de ligne desrc/: toute référence de ligne d'une session ≤ 60 est périmée (table de correspondance des références encore vivantes dansdocs/sessions/session-61.md).- Couverture (
coverage.py) :switch_capture_cli.py99 % (1062-1063 seules restantes, ex-998-999 avant reformatage — ligne__main__couverte depuis la session 55, détail :docs/sessions/session-55.md),switch_capture_core.py88 % (177 lignes, 84→88 % en session 64, lot mirroring de l'issue #68 ; 80→84 % en session 62 — triage complet des lignes restantes par fonction dansdocs/sessions/session-62.md),switch_capture_gtk.py10 % (attendu — code GTK4, non couvert par la suite pytest par construction, voir @docs/features-backlog.md) - Session 55 : fusion de deux branches de développement divergentes
(session 53 menée en parallèle dans deux conversations distinctes à
partir du même point de départ) — détail :
docs/sessions/session-55.md - Session 56 : audit ciblé, via Context7, de la documentation à jour de
6 dépendances externes (PyGObject/GTK4, netmiko, paramiko,
scp,keyring, pykeepass, ruff) contre l'usage actuel du code — 5 candidats identifiés (dette technique GTK4, progression SCP en direct, keepalive netmiko, keyfile PyKeePass,pyproject.tomlruff/pytest/ coverage), tous ajoutés au backlog, aucun implémenté (demande explicite : audit + proposition seulement). Suite de tests revérifiée sans aucune modification de code : toujours 490 passés, même échec préexistant, mêmes 10 skips — détail :docs/sessions/session-56.md - Session 57 : traitement du point 7 ci-dessous (keepalive netmiko,
keepalive=30surconnect_switch()) — un seul candidat de l'audit Context7 traité par session, comme demandé.tests/test_connect_switch.pyétendu (3 tests modifiés + 1 nouveau) ; suite complète revérifiée sans régression : 491 passés, même échec préexistant, mêmes 10 skips — détail :docs/sessions/session-57.md - Session 58 : deux volets — (1) nettoyage complet de la dette
ruffjamais traitée en un seul passage sur l'ensemble du dépôt (53→0 erreurs :C408/RUF100corrigés par autofix,PLW1510par ajout manuel decheck=False,BLE001par# noqajustifié après relecture individuelle des 22 sites, tous des frontières d'exception délibérées) ; (2) sous-piste 1 du point 4 ci-dessous traitée : les imports optionnels en tête deswitch_capture_core.pysont désormais couverts (tests/test_optional_imports_absent.py, 5 nouveaux tests, via une copie de module isolée plutôt qu'unimportlib.reload()— ce dernier a été essayé puis rejeté après avoir casséisinstance()ailleurs dans la suite, voir détail). 496 passés, même échec préexistant, mêmes 10 skips — détail :docs/sessions/session-58.md - Session 59 : traitement du point 8 ci-dessous (fichier de clé KeePass
additionnel,
--keepass-keyfile) — un seul candidat de l'audit Context7 traité, comme les sessions 57/58. Cœur + CLI étendus (_open_keepass_dbet les 3 fonctions publiques, nouveau flag surcapture/inspect, fil de transmission complet jusqu'àbuild_config/_apply_password_keyring_actions) ;tests/test_keepass_password.pyétendu (13 nouveaux tests, dont un faux backend qui vérifie réellement la valeur reçue) ; deux tests de complétude documentaire (test_config_example_completeness.py,test_usage_md_completeness.py) mis à jour en conséquence,USAGE.mdcomplété. GUI volontairement pas étendue cette session (scope cœur + CLI). 509 passés, même échec préexistant, mêmes 10 skips — détail :docs/sessions/session-59.md - Session 60 : deux candidats de l'audit Context7 traités (le
ruff checkdemandé en début de session était déjà à 0 erreur depuis la session 58, rien à corriger). (1) Point 5 :pyproject.tomlcréé, limité aux sections[tool.*](ruff/pytest/coverage), sans[build-system]/[project]— le dépôt reste non packagé ; vérifié par exécution réelle que les commandes sans flags donnent des résultats identiques. (2) Point 6 :progress_callbackoptionnel surscp_get/scp_put+make_scp_progress_logger()qui journalise par paliers de 10% par fichier (plutôt qu'à chaque bloc), câblé sur les deux appelants réels ; 12 nouveaux tests, dont deux qui ont trouvé un vrai bug (ligne « 0% » parasite avant le premier palier) corrigé dans la foulée. Deux faux backends d'autres fichiers de test mis à jour (signature publique modifiée). 522 passés, même échec préexistant, mêmes 10 skips — détail :docs/sessions/session-60.md - Session 61 : deux volets. (1) Dernier reste de dette
rufftraité —ruff checkétait déjà à 0 depuis la session 58, c'estruff formatqui restait en attente : 15 fichierssrc//tests/reformatés, AST vérifié identique avant/après sur les 44 fichiers Python du dépôt ; le 16ᵉ fichier signalé était un journal de session archivé (ruffreformate aussi les blocs```pythondu Markdown), exclu du seul formateur par[tool.ruff.format]pour ne pas réécrire l'historique. (2) Sous-piste 2 du point 4 ci-dessous close : la branche « modèle inconnu » de_prepare_switchest confirmée inatteignable, pas couverte pour autant — ses 3 prémisses sont verrouillées partests/test_prepare_switch_model_invariant.py(110 tests paramétrés depuisMODEL_PROFILES, dont un qui interdit toute réaffectation decfg.modeldanssrc/), validées par 3 mutations. 632 passés, même échec préexistant, mêmes 10 skips — détail :docs/sessions/session-61.md - Session 62 : poursuite du point 4 — triage des 291 lignes non couvertes
de
switch_capture_core.pypar fonction (croisementcoverage json× AST), jamais fait jusqu'ici ; puis traitement du plus gros bloc désigné par ce triage,UninstallThread.run(50 lignes, 17 % de la dette, à 0 % parce quetest_uninstall_confirm.pyremplace délibérément la classe par un faux thread).tests/test_uninstall_thread.py, 29 tests. A trouvé un vrai bug : sur le chemin de découverte du.bin(sansfeature_bin_path), le préfixe média était capturé dans le nom de fichier et la commande devenaitinstall deactivate feature flash:/flash:/..., rejetée par le switch — format réel vérifié contre la command reference HPE avant de conclure, correctif prouvé porteur par restauration de l'ancienne expression.core80→84 %, 661 passés, même échec préexistant, mêmes 10 skips — détail :docs/sessions/session-62.md - Session 63 : « on passe uv pour remplacer poetry » — vérifié d'abord
que le projet n'a jamais utilisé Poetry (aucune trace, aucun
poetry.lock) ; interprété comme adopteruvpour la gestion des dépendances de développement, en remplacement depip+requirements.txt/requirements-dev.txt(supprimés).[project]ajouté àpyproject.toml(aucun[build-system],[tool.uv] package = falseexplicite) : documentationuvofficielle vérifiée avant d'écrire quoi que ce soit (https://docs.astral.sh/uv/concepts/projects/config/#build-systems) pour confirmer que cela ne revient pas sur le choix « pas de packaging » (session 60/tests/conftest.py) — c'est l'absence de[build-system], pas celle de[project], qui en décide. Deux groupes de dépendances (devsynchronisé par défaut,qualitysur demande, reprenant la distinction déjà en place).uv.lockgénéré (65 paquets ;netmikos'y résout en deux versions selon la plage Python — utile pour le plancherpython3.9réel d'el8, jamais garanti par l'ancienrequirements.txtnon verrouillé). Toutes les commandes revérifiées par exécution réelle (uv sync,uv run pytest -q: 661 passés inchangé ;uv run --group quality ruff check ./coverage report -m: résultats identiques à la session 62).README.md/docs/architecture.md/docs/features-backlog.mdmis à jour partout où ils citaient les fichiers supprimés..gitignoreajouté (n'existait pas), couvre aussi les fichiersswitch_capture*.logparasites repérés en session 62. Point 4 (couverture) non touché cette session — détail :docs/sessions/session-63.md - Backlog et limites connues à jour : @docs/features-backlog.md
Aucun point numéroté restant. Candidats pour la prochaine session (un seul à traiter à la fois) :
- Mesurer la durée SCP réelle contre un switch physique dès qu'un accès est disponible — seul point technique encore ouvert (voir « Pas fait » dans le backlog).
- Corriger la limite connue (documentée depuis la session 08, verrouillée
par des tests dédiés en session 50) sur les alias de détection modèle
5510/5520: forme collée ("5510HI") plutôt que le format à tiret réel HPE confirmé pour 5130/5140 ("5130-28-HI") — nécessite un exemple réel de sortiedisplay versionpour un 5510/5520 avant de corriger à l'aveugle (aucun disponible dans ce dépôt à ce jour). - Relecture linguistique du
.poen_US par une personne anglophone native — tâche non automatisable, en attente depuis la session 33. - Poursuivre l'audit de couverture (session 51-52-53-54-58-61-62) sur les
lignes restantes de
switch_capture_core.py(88 %, 177 lignes non couvertes depuis la session 64). Le triage par fonction, jamais fait avant la session 62, est désormais disponible dansdocs/sessions/session-62.md: tableau ligne/fonction complet et regroupement en 3 familles par coût de test. À reprendre par là plutôt que par la liste de plages brutes dedocs/sessions/session-54.md, devenue périmée (numéros décalés en session 61, contenu couvert en session 62). Candidat naturel de continuation, pas bloqué par un facteur externe contrairement aux trois points ci-dessus. Lot mirroring (configure_gre_mirror,MirrorThread.run,teardown_mirror,configure_local_mirror) traité en session 64 (issue #68,tests/test_mirror_port_sequences.py, plus aucune ligne non couverte dans ces quatre fonctions) : reprendre par les familles suivantes du triage de la session 62. Trois sous-pistes précises traitées, aucune encore ouverte :Les imports optionnels en tête de fichier (✅ Fait (session 58, 13/09/2026) —netmiko/paramiko/keyring/pykeepass, lignes 33-59)tests/test_optional_imports_absent.py(5 tests), via une copie de module isolée (importlib.util), pas unimportlib.reload()du module partagé : ce dernier a été essayé puis rejeté après un essai réel (casseisinstance()ailleurs dans la suite selon l'ordre de collecte pytest — voirdocs/sessions/session-58.mdpour le détail du piège et de la correction).✅ Fait (session 61, 15/09/2026) — confirmé inatteignable, ligne délibérément laissée non couverte (2415 après reformatage) comme 1062-1063 côté CLI. Les 3 prémisses du raisonnement sont verrouillées parSetupAndCaptureThread._prepare_switch, ligne 2373 (if model not in MODEL_PROFILES) : probablement du code mort — à confirmer/documenter plutôt qu'à forcer par une mutation post-construction du dataclass.tests/test_prepare_switch_model_invariant.pyplutôt que consignées en prose :detect_modelne renvoie queNoneou une clé réelle,Configrefuse tout modèle hors clés (alias compris), et aucun code desrc/ne réaffectecfg.modelaprès construction — cette dernière vérifiée sur le source, donc une régression future échoue au lieu de rendre l'analyse silencieusement fausse. Une mutation dedetect_modelatteint réellement la ligne : ce n'est pas du code impossible à exécuter, c'est une garde défensive neutralisée en amont — à conserver telle quelle, pas à supprimer.✅ Fait (session 62, 15/09/2026) —UninstallThread.run(50 lignes, le plus gros bloc jamais exercé du fichier)tests/test_uninstall_thread.py(29 tests), classe intégralement couverte. À 0 % jusque-là parce quetest_uninstall_confirm.pyremplace délibérément la classe par un faux thread : les deux fichiers sont complémentaires, le nouveau teste ce que l'ancien remplace. A trouvé un vrai bug (préfixeflash:/dupliqué dans la commande de désactivation sur le chemin de découverte du.bin), corrigé dans la foulée — détail :docs/sessions/session-62.md. La piste connexe « reformatage complet du dépôt » est close (session 61, voir « État courant »).
- (Session 56, audit Context7)
✅ Fait (session 60, 14/09/2026) —pyproject.tomlcentralisant[tool.ruff](line-length = 120),[tool.pytest.ini_options](testpaths = ["tests"]) et[tool.coverage.run](source = ["src"]) — évite de répéter les mêmes flags sur chaque commande de la section « Commandes de qualité » ci-dessus. Non bloqué, risque quasi nul (fichier de config seul, aucune ligne de code touchée) ; point d'attention : documenter explicitement que ce fichier ne change pas le choix délibéré « pas de packaging » (voirtests/conftest.py) — voirdocs/features-backlog.mdpour le détail complet.pyproject.tomlcréé à la racine, limité à[tool.ruff]/[tool.pytest.ini_options]/[tool.coverage.run], aucun[build-system]/[project]. Vérifié par exécution réelle (ruff check .,ruff format --check .,pytest -q,coverage run -m pytest -qsans aucun flag) : résultat identique aux invocations avec flags.tests/conftest.pymis à jour. (Mise à jour session 63 :[project]ajouté pour la gestion des dépendances viauv—[build-system]toujours absent, le choix « pas de packaging » n'a pas changé, seule sa mise en œuvre précise l'a fait. Voir « État courant » etdocs/sessions/session-63.md.) - (Session 56, audit Context7)
Callback de progression SCP en direct (✅ Fait côté cœur (session 60, 14/09/2026) —scp.SCPClient(progress=...), jamais utilisé aujourd'hui parscp_get/scp_put) — permettrait un pourcentage par fichier remonté jusqu'à la page « Journal » de la GUI, en complément du débit moyen déjà calculé après coup. Non bloqué, testable en isolation (callback injecté, pas de switch réel requis).progress_callbackoptionnel surscp_get/scp_put+make_scp_progress_logger()(journalisation par paliers, suivi par nom de fichier), câblé sur_push_feature_fileetCaptureRotationThread. 12 nouveaux tests ; un bug de palier « 0% » trouvé par ces tests et corrigé. ✅ Remontée jusqu'à la page « Journal » faite en session 65 (issue #70) :on_progressdemake_scp_progress_loggerpublie chaque palier dansSharedState.scp_progress(ScpProgress), affiché par_refresh_journal(format_scp_progress) sur le thread GTK — aucun appel GTK depuis le thread de transfert. Côté CLI,-vaffichait déjà les paliers DEBUG. Voirdocs/sessions/session-60.mdetdocs/sessions/session-65.md. - (Session 56, audit Context7)
Keepalive netmiko (paramètre✅ Fait (session 57, 12/09/2026) —keepalivedu profilhp_comware, absent du dictdevicedeconnect_switch()) sur la connexion de polling longue durée (_poll_conn) — même famille que le bug SCP corrigé le 23/08/2026 (sessions fermées côté switch).keepalive=30ajouté au dictdevice(constanteNETMIKO_KEEPALIVE_SECONDS) ; l'effet réel sur un switch physique reste à confirmer, comme la mesure SCP réelle du point 1 — voirdocs/sessions/session-57.md. - (Session 56, audit Context7)
Support d'un keyfile PyKeePass en plus du mot de passe maître (✅ Fait (session 59, 13/09/2026) — cœur (PyKeePass(path, password=..., keyfile=...), non exposé aujourd'hui par_open_keepass_db) —--keepass-keyfileoptionnel, rétrocompatible. Non bloqué, testable avec le faux backend déjà utilisé partest_keepass_password.py._open_keepass_dbet les 3 fonctions publiques) + CLI (--keepass-keyfilesurcapture/inspect, fil de transmission complet) étendus, 13 nouveaux tests. GUI volontairement pas étendue (scope cœur + CLI cette session) — voirdocs/sessions/session-59.md. - (Session 56, audit Context7)
Migration: fait (issue #69). Versions relevées le 11/10/2026 : Debian 12 GTK 4.8.3, Debian 13 4.18.6, Rocky/RHEL 9 4.12.3. Debian 12 étant < 4.10, la nouvelle API est choisie à l'exécution (Gtk.FileChooserNative/Gtk.MessageDialogversGtk.FileDialog/Gtk.AlertDialog_HAS_FILE_DIALOG,_HAS_ALERT_DIALOG) avec repli sur l'ancienne ; les 3 confirmations à widgets (IP à retaper, nom de modèle, liste de modèles) passent par_open_form_dialog, uneGtk.Windowmodale non dépréciée. Tests :tests/test_gui_dialogs.py(21 tests, Xvfb, sautés sans PyGObject comme les autrestest_gui_*).
Dépendances gérées par uv depuis la session 63 (remplace pip +
requirements.txt/requirements-dev.txt, supprimés — voir
docs/sessions/session-63.md). pyproject.toml reste par ailleurs ce
qu'il était depuis la session 60 : line-length/testpaths/source
y sont centralisés, donc les commandes ci-dessous fonctionnent sans
flags explicites. Les variantes avec flags (--line-length 120,
tests/, --source=src) restent équivalentes et documentées dans
docs/sessions/session-60.md — à garder en tête si un jour ce fichier
disparaît ou est modifié par erreur. Il porte aussi, depuis la session
61, un [tool.ruff.format] exclude sur docs/**/*.md (ne pas reformater
les blocs de code des journaux de session archivés).
Deux groupes de dépendances ([dependency-groups], PEP 735) :
dev (pytest + keyring/pykeepass, nécessaires pour que la suite passe
sans rien sauter d'autre que les 10 tests GTK4) est synchronisé par
défaut par uv sync/uv run ; quality (ruff + coverage) ne l'est
pas — comportement uv documenté pour tout groupe autre que dev,
pas une option choisie ici — et demande --group quality explicite,
comme ruff/coverage étaient déjà hors requirements-dev.txt
auparavant (« optionnel — audit ponctuel seulement »). uv run invoque
la commande demandée après avoir vérifié que uv.lock correspond à
pyproject.toml et que .venv/ correspond à uv.lock — pas d'étape
d'installation séparée à retenir.
uv sync # groupe dev seulement (défaut)
uv run pytest -q
uv run --group quality coverage run -m pytest -q && uv run --group quality coverage report -m # audit ponctuel
uv run --group quality ruff check .
uv run --group quality ruff format --check .
python3 -m py_compile <fichiers modifiés> # hors venv, inchangéDepuis la session 61, le dépôt entier est conforme aux deux
commandes ruff : les lancer sur . plutôt que sur les seuls fichiers
modifiés ne coûte rien et empêche une nouvelle dette de s'accumuler sur
plusieurs sessions, comme cela s'est produit deux fois (53 erreurs
check en session 58, 16 fichiers format en session 61).
Ajouter une dépendance : uv add <paquet> (runtime, [project.dependencies]),
uv add --dev <paquet> (groupe dev) ou uv add --group quality <paquet>
— jamais éditer pyproject.toml/uv.lock à la main pour ça, uv
maintient les deux fichiers en cohérence. .venv/ n'est jamais livré
dans le zip horodaté (recréé par uv sync, voir .gitignore ajouté en
session 63).
- Une seule tâche faisable traitée par réponse, tirée du backlog.
- Livraison en zip nommé
switch-capture-{YYYYMMDD-HHMMSS}.zip, avec ceCLAUDE.mdetdocs/features-backlog.mdà jour dedans. - En fin de session : mettre à jour « État courant » et « Prochaine
feature » ci-dessus, puis ajouter
docs/sessions/session-NN.mdpour le raisonnement détaillé (voirdocs/sessions/session-50.mdpour le format) — ne pas réécrire l'historique dans ce fichier-ci.
- Architecture et choix techniques (comment/pourquoi) :
docs/architecture.md - Historique détaillé, session par session :
docs/sessions/(partir de l'index, ne pas tout charger) - Documentation utilisateur :
src/docs/(USAGE.md,CAPTURE-METHODS.md,INSTALL.md)