Classification des diffs VRT : comment VisualQ apprend bug vs évolution
Les tests de régression visuelle excellents pour une question : ces deux captures diffèrent-elles ?
Ils sont faibles sur la suivante : cette différence est-elle un bug, ou une évolution assumée ?
Sans aide, les humains ouvrent chaque paire viewport × navigateur en échec, devinent l’intention, approuvent ou investiguent, et recommencent. Ça ne scale pas.
La classification de diffs ajoute une seconde couche au-dessus du moteur pixel de VisualQ. Après un run en échec, VisualQ suggère une classe pour chaque zone modifiée, expose le signal qui la motive, et — seulement quand le risque statistique est borné — marque certaines zones pour une revue plus légère. La détection reste pixel. La classification est la boucle d’apprentissage.
Deux classes, pas trois
Chaque prédiction et chaque label humain utilise l’une de ces deux classes :
- Bug — quelque chose est cassé. Action typique : investiguer et corriger.
- Évolution — un changement produit ou design volontaire. Action typique : approuver et mettre à jour la baseline.
Le bruit d’environnement — anti-aliasing, widgets tiers, quirks mono-navigateur — est classé bug. Du point de vue de l’utilisateur, un artefact de rendu reste quelque chose de cassé, même quand la cause est le navigateur plutôt que le commit. Le correctif sera peut-être une règle de comparaison plutôt qu’un fix code, mais ce n’est pas une évolution, et prétendre le contraire apprend la mauvaise leçon au classifieur. Les égalités et les cas sans signal tombent aussi sur bug : la direction sûre.
L’unité, c’est la zone, pas la capture
Une page change rarement d’une seule manière à la fois. Une release peut déplacer le hero volontairement et casser un bouton dans l’en-tête sur le même déploiement : un verdict unique pour toute la capture est faux dans les deux sens.
VisualQ classe donc une zone modifiée à la fois. Le moteur de comparaison sait déjà quelles sections de page ont bougé ; chacune reçoit sa classe, son signal et sa décision humaine. Une ligne de la matrice de résultats se lit pour ce qu’elle est — par exemple deux zones en bug et une en évolution — au lieu de s’effondrer en une seule supposition.
Les zones gardent une identité stable d’un run à l’autre (l’identifiant de section, un hash de son sélecteur, ou sa position en dernier recours) : une correction faite aujourd’hui s’applique encore à la même section demain.
Ce que ce n’est pas
La classification ne remplace pas la capture, la comparaison anti-shift, ni le gate fail/pass. Une suggestion « évolution » sur une zone en échec ne promeut pas la baseline toute seule en v1 — labelliser et approuver restent deux actions distinctes.
Les pixels trouvent encore le changement. Le classifieur explique et priorise.
Le flywheel d’apprentissage
1. Un run VRT se termine et un snapshot figé est construit par zone modifiée.
2. Le classifieur écrit une classe suggérée sur chaque zone non labellisée.
3. Quand un humain confirme ou corrige une zone, ce label est stampé et aussi écrit — anonymisé — dans une mémoire partagée.
4. Les runs suivants utilisent cette mémoire, avec un poids plus fort pour les voisins du même projet.
La correction est le produit. Un clic pour dire « non, celle-là c’est un bug » est à la fois la façon la plus rapide de relire un run et le seul signal d’apprentissage que le modèle reçoit.
Jour un, sans labels : uniquement des heuristiques. Après assez d’historique labellisé : la classification par plus proches voisins devient disponible. Après assez de calibration locale : un projet peut débloquer l’auto-triage.
Cascade hybride (comment naît une suggestion)
Tout tourne in-process dans la stack TypeScript de VisualQ. Pas de service Python externe sur ce chemin.
- D’abord, chaque zone devient un vecteur de features numériques versionné (ce qui a changé dans cette zone, magnitude, stats spatiales, signaux DOM/shift, historique, viewports frères, intent GitHub / JIRA / Figma si présents, champs Smart Diff distillés). Les intégrations absentes sont vraiment absentes — jamais de faux zéros.
- Les heuristiques tournent toujours : règles déterministes dès le jour un (bruit mono-navigateur, pixels isolés, élément interactif modifié, signaux d’intention design). Elles n’auto-triagent jamais.
- Le k-NN tourne quand le contexte labellisé suffit : voisins pondérés sur l’historique anonymisé, voisins locaux du projet surpondérés.
- Une porte conformal par projet décide si une suggestion est assez sûre pour l’auto-triage — en substance : parmi les zones à bas risque, la chance mesurée de rater un vrai bug reste dans une borne serrée.
- Sur les zones différées, un second avis optionnel est distillé depuis l’analyse Smart Diff déjà présente. Il ne remplace jamais silencieusement la classe principale.
Chaque triage porte une source explicite : heuristique ou modèle. Pas de mélange silencieux sur une même zone.
Confidentialité : apprendre globalement sans fuite client
Les lignes d’apprentissage vivent dans un store dédié qui ne contient que :
- Version de features et valeurs numériques
- Label, source du label, horodatage
- Hashes salés de l’identité org et projet
Jamais stockés : URLs, noms de scénarios, images, texte libre, ou ids org/projet bruts. Les SDK clients ne peuvent pas lire ce store. Sans salt configuré, les écritures d’apprentissage sont ignorées ; la classification heuristique continue.
Autonomie progressive
- Suggestion seule (défaut) — le viewer montre la classe et le signal déterminant de chaque zone. L’humain décide toujours.
- Éligible à l’auto-triage — après assez de paires de calibration locales et un seuil de risque valide, le projet peut activer le toggle.
- Auto-triage activé — les zones sous le seuil conformal sont marquées pour une revue plus légère. La v1 ne promeut pas les baselines automatiquement.
La calibration se construit en ombre : quand une prédiction existe et qu’un humain labellise ensuite la zone, la paire (probabilité de bug, classe prédite, label humain) est enregistrée pour ce projet.
Où vous le voyez dans VisualQ
- Après chaque run VRT, le triage tourne en arrière-plan et ne bloque jamais le pipeline de test.
- Le panneau de triage du viewer liste chaque zone modifiée avec sa classe, le signal qui la motive, et une correction en un clic.
- La matrice de résultats compte les zones par ligne — par exemple deux bug et une évolution — dans les mêmes couleurs que le viewer.
- Corriger une zone, ou approuver une ligne, alimente la boucle d’apprentissage.
- Les agents de code reçoivent le même découpage via MCP : un assistant peut nommer la section cassée plutôt que la page entière.
- Les équipes peuvent relancer le triage et gérer l’éligibilité auto-triage via les APIs projet.
Une phrase à retenir
VisualQ détecte déjà les différences visuelles. La classification pointe chaque zone modifiée et la qualifie de bug ou d’évolution — à partir des corrections passées de votre équipe, anonymisées — avec un frein dur pour que l’automatisation ne s’active que lorsque rater un vrai bug est statistiquement rare.
Les pixels trouvent le changement. La cascade explique et priorise. Un humain peut toujours renverser la décision, et c’est exactement cette correction qui entraîne le classifieur.
*Ingénierie produit VisualQ — juillet 2026.*