L'Audit Multi-Agents

Braque soixante-sept agents en lecture seule sur une base de code, découpe-la en huit, et laisse chaque agent regarder exactement une chose. Le résultat n'est pas un avis — c'est un seul rapport priorisé avec un score de dette technique. Voici comment l'audit tourne, et ce que deux runs réels ont trouvé.

Version 1.0 · Mis à jour

01

Une lecture, beaucoup d'yeux

Un audit est un fan-out en lecture : beaucoup d'agents qui observent, rien qui change. Le schéma est mécanique. On partitionne la base de code en tranches disjointes, on assigne chaque tranche à chaque domaine d'analyse, on exécute la grille d'agents qui en résulte par vagues, puis on remet tous leurs constats à un seul agent de synthèse qui les condense en un rapport. La même machinerie, parcourue en sens inverse, devient la remédiation autonome — voir le retour d'expérience compagnon pour la moitié écriture.

67agents parallèles
8domaines d'analyse
16vagues
0–100score de dette

Chaque agent tourne en arrière-plan et écrit ses constats dans un fichier ; le contexte principal reste propre et reçoit une notification à la fin. Un manifest.json enregistre quels agents sont terminés et lesquels restent en attente, si bien qu'un run interrompu reprend à la dernière vague au lieu de tout recommencer. Rien de ce que les agents touchent n'est modifié — la passe entière est non destructive par construction.

02

Huit domaines, une passe

Chaque domaine est une lentille — un jeu fixe de questions qu'un agent pose au code qu'il a sous les yeux. Huit lentilles balaient chaque partition ; une neuvième, la dérive axiale, reçoit sa propre passe en deux temps (voir §4). Les lentilles ne se recouvrent pas : deux agents ne remontent jamais la même classe de problème sous deux angles.

DomaineCe qu'il cherche
ArchitectureViolations de couches, dépendances circulaires, couplage
SécuritéCatégories OWASP, gestion des secrets, vecteurs d'injection
Code smellsGod classes, fonctions trop longues, violations DRY
TypageUsage de Any, annotations manquantes, type: ignore
Patterns asyncRaces, appels bloquants dans l'async, fuites de ressources
Gestion d'erreurexcept nus, exceptions avalées, contexte perdu
Qualité des testsTrous de couverture, patterns flaky, sur-usage de mocks
Dette techniqueTODO, API dépréciées, nombres magiques
Dérive axialeDuplication sur le mauvais axe — le piège N×M (§4)
03

Partition et vague

La base de code est découpée en partitions source et partitions de test — des tranches par zone de code, pas par taille. Un agent reçoit une cellule (domaine × partition) : une lentille sécurité sur les adapters, une lentille typage sur le cœur du hub. Chaque prompt reste étroit et chaque constat se rattache à un endroit. Les agents se lancent par cinq — assez de parallélisme pour finir en minutes, pas au point que la synthèse finale se noie dans le contexte.

1
Partitionner
Découper le source en P1–P8 et les tests en T1–T6 par zone, pour que deux agents ne lisent jamais les mêmes fichiers.
2
Lancer une vague
Cinq agents domaine×partition démarrent en arrière-plan, chacun avec une lentille et une tranche.
3
Collecter
Attendre la fin de la vague, puis écrire les constats de chaque agent dans son propre fichier, au même format.
4
Pointer
Mettre à jour le manifeste — agents terminés et en attente — pour que le run soit reprenable d'ici.
5
Synthétiser
Un dernier agent lit tous les fichiers de constats et émet un seul rapport priorisé.

Partitionner par zone de code rend l'audit peu coûteux à lire à la fin. Chaque constat porte déjà un fichier et une ligne ; la synthèse est une fusion, pas une nouvelle enquête.

04

Dérive en deux temps, recherche en second avis

La dérive axiale, c'est la duplication d'une préoccupation transverse à travers des frères sur l'axe non primaire — la même logique de retry copiée dans le store NATS et le store Redis, le même contrôle d'auth collé dans trois adapters. C'est la seule classe de problème qu'une lentille mono-fichier ne peut pas voir, alors elle reçoit deux passes et une confirmation.

1
Structurel — importlinter
Exécute les contrats de couches : imports inter-couches interdits, dépendances circulaires, violations de contrat. Un code de sortie non nul est un constat ferme.
2
Sémantique — axial-adr-review
Un agent en lecture seule confronte un diff à l'ADR axial du projet et signale la duplication sur le mauvais axe que le linter ne sait pas exprimer.
3
Confirmer — recherche sémantique
La recherche d'index de code (ccc) mesure à quel point les frères suspectés se ressemblent vraiment, transformant une intuition en score de similarité.

Cette troisième étape sert aussi d'arbitre transverse. Tout constat qu'une lentille primaire remonte — un smell de sécurité, une violation DRY — est confronté à la recherche sémantique, et le score décide de son sort :

SimilaritéVerdict
> 0,90Duplication confirmée / pattern exact
0,70 – 0,90Probable — revue manuelle requise
0,50 – 0,70Signal faible — probable faux positif
< 0,50Écarter
constat(domaine)  →  ccc.confirme(top-5)
                  →  aucun match top-5  ⇒  sévérité −1   # rétrograder un constat non confirmé

Un constat qu'aucun second avis ne corrobore n'est pas supprimé — il est rétrogradé. L'audit préfère un constat sous-évalué à un constat faux mais sûr de lui.

05

Le rapport qu'il produit

Soixante-six fichiers de constats ne valent rien comme livrable. L'agent de synthèse les lit tous et écrit un seul AUDIT-SUMMARY.md — le seul artefact qu'un humain est censé ouvrir en premier.

  • Résumé exécutif. Santé globale, posture de sécurité, état de la couverture de tests, les items de dette en tête.
  • Paniers de sévérité. Chaque constat trié en P0 (critique), P1 (haut), P2 (moyen), P3 (bas) — avec fichier et ligne.
  • Synthèse de dérive axiale. Contrats tenus contre rompus, pièges N×M, et lesquels la recherche sémantique a confirmés.
  • Tableau de bord métriques. Nombre de problèmes par domaine, distribution des sévérités, et un unique score de dette technique sur une échelle 0–100 où 100 est immaculé.
  • Actions recommandées. Priorisées, chacune avec une estimation d'effort, plus un top 10 des quick wins : fort impact, faible effort.

Le score de dette est le seul nombre suivable d'une release à l'autre. Tout le reste dit quoi corriger ; le score dit si la base de code se porte mieux.

06

Deux audits réels

Le schéma n'est pas théorique. Roxabi le passe sur sa propre factory d'agents, et les recaps sont en accès ouvert. Deux méritent une lecture complète.

A — Audit qualité, 2 juin 2026. Un balayage complet de la base de code de la factory : 3 291 fichiers, chaque domaine, chaque partition.

3 291fichiers audités
1 258problèmes trouvés
72/100score de dette
11 / 0contrats tenus / rompus

La répartition de sévérité était 2 · 172 · 664 · 427 sur P0–P3, et les onze contrats import-linter ont tenu — l'architecture était saine, la dette était du volume, pas un effondrement. Les deux P0 sont le genre de bug qu'un simple run de tests masque. L'un était une méthode process_one de 165 lignes fusionnant les chemins streaming et non-streaming, les hooks de processeur, les mises à jour d'ID de session et la gestion d'erreur dans un seul bloc illisible. Le plus tranchant : _log_turn attrapait toute exception, la journalisait et retournait — et l'appelant faisait alors ack() sur le message JetStream. Une écriture SQLite échouée perdait donc définitivement le tour de conversation, avec une ligne de log au vert pour le masquer. Vert ≠ correct, trouvé en lisant, pas en exécutant. Cet audit est le précurseur que le rapport Remédiation Autonome a ensuite soldé.

B — Audit codebase, 18 mai 2026. Une passe plus étroite, transversale, sur trois axes — pureté hexagonale, mutualisation, simplification — et un résultat d'une autre nature.

Celui-ci n'a produit aucun chiffre de dette ; il a produit une carte de refactor. Le verdict était rassurant : zéro violation import-linter, couches propres, packages isolés — la dette était localisée, pas systémique. Trois chantiers ressortaient : ~350 lignes de code mort issu d'un cluster de routage retiré, supprimables sans risque ; une poignée de ports implicites éparpillés hors de core/ports/ ; et un cycle de heartbeat NATS copié verbatim à travers quatre clients, qui appelait une classe de base partagée. Ce qui rend le recap intéressant, c'est sa rigueur sur les faux positifs — deux duplications apparentes se sont révélées être des role interfaces (Fowler) délibérées, distinctes des driven ports (Cockburn), renommées pour casser l'homonymie plutôt que fusionnées. Un audit incapable de distinguer la dérive de l'intention génère du travail inutile ; celui-ci a tracé la ligne et livré sept PR le lendemain.

Chaque recap — balayages qualité, audits par cluster, revues d'isolation de tokens — vit dans un seul dossier, aux côtés du playbook qui définit la méthode.

07

Ce qui passe à l'échelle

Le nombre d'agents suit la taille de la base de code, pas l'ambition. La largeur de vague reste à peu près constante ; seul le nombre de vagues croît.

Base de codeAgentsTaille de vagueDurée
< 50 fichiers184~12 min
50 – 200 fichiers344~22 min
200 – 500 fichiers505~45 min
500+ fichiers69+5~65+ min

Les constats sont propres à un dépôt. La forme, non — voici les parties qui se transposent à n'importe quelle base de code.

  • Partitionner par zone. Un agent, une lentille, une tranche. Aucun travail en double, chaque constat épinglé à un endroit.
  • Vagues de cinq. Assez parallèles pour être rapides, assez bornées pour que la synthèse tienne dans un seul contexte.
  • Suivi par manifeste. Un run interrompu reprend à la dernière vague au lieu de tout recommencer.
  • Dérive en deux temps. Un linter attrape la structure ; un agent sémantique attrape l'intention ; la recherche confirme les deux.
  • Filtrage en second avis. Un constat non confirmé est rétrogradé, pas asséné — fort rappel sans la taxe des faux positifs.

L'audit n'est que la moitié lecture. Une fois le rapport là, le même fan-out tourne à l'envers pour le solder — ce run, c'est le rapport Remédiation Autonome. Lire, puis écrire, entièrement sur le compte rendu.