Aller au contenu

veaf-tools doctor — format du bloc collable

Public visé : les développeurs des lots qui consomment la sortie de doctor — l'analyse de journal (FEAT-SUPPORT-LOG-ANALYSIS) qui l'intègre à son propre bloc de rapport, et la prise en charge de signalement (FEAT-SUPPORT-BUG-INTAKE) qui le relit.

Schéma : veaf-tools-doctor/1. — 🇬🇧 diagnostic-block.en.md.

Pourquoi un format

Le bloc voyage à travers Discord ou une issue GitHub, copié à la main par quelqu'un qui ne le relira pas, et il est relu par une machine à l'autre bout. Ce n'est donc ni un affichage ni un fichier : c'est un contrat, versionné, que ni le producteur ni les consommateurs ne peuvent changer seuls.

L'implémentation vit dans src/python/veaf-tools/veaf_libs/diagnostics.py, producteur (DiagnosticReport.to_block) et lecteur (parse_block) dans le même module — pour qu'ils ne puissent pas diverger — et le test d'aller-retour est dans test/python/veaf_libs/test_diagnostics.py.

Structure

=== VEAF-TOOLS DOCTOR BEGIN ===
schema: veaf-tools-doctor/1
generated: 2026-09-05T09:39:29Z
tool.version: 6.19.0
...
--- recent-errors ---
2026-09-03 21:05:52,533 - veaf-tools - ERROR - Failed to evaluate time expression
Traceback (most recent call last):
ValueError: ...
--- recent-errors end ---
=== VEAF-TOOLS DOCTOR END ===

Trois règles, et rien d'autre :

  1. Le bloc est délimité par === VEAF-TOOLS DOCTOR BEGIN === et === VEAF-TOOLS DOCTOR END ===. Il arrive au milieu d'autre chose (un message, des barrières de code) : un lecteur cherche ces deux lignes, il ne suppose pas que le bloc commence à la première ligne.
  2. Entre les deux délimiteurs, chaque ligne est clef: valeur. La séparation se fait sur le premier : — une valeur peut en contenir d'autres (un chemin Windows, un horodatage). Une clef et une valeur tiennent sur une seule ligne : le producteur écrase tout saut de ligne par une espace, faute de quoi une valeur multi-lignes reviendrait sous la forme de deux champs, dont un que personne n'a écrit.
  3. La section optionnelle encadrée par --- recent-errors --- et --- recent-errors end --- contient du texte brut. Un enregistrement commence à une ligne d'en-tête (AAAA-MM-JJ HH:MM:SS,mmm - <logger> - <NIVEAU> -) ; tout ce qui suit lui appartient, ce qui permet à une trace d'appels de survivre entière.

Les champs

L'ordre est celui de FIELD_ORDER dans diagnostics.py, et il est stable.

Clef Exemple Vaut unknown quand
schema veaf-tools-doctor/1 jamais
generated 2026-09-05T09:39:29Z jamais
tool.version 6.19.0 le paquet n'est pas installé et le fichier de version est absent
tool.packaging frozen ou source jamais
tool.executable C:\Users\<user>\...\python.exe l'interpréteur ne se nomme pas
tool.python 3.13.15 jamais
machine.os Windows-11-10.0.26200-SP0 la plateforme ne répond pas
machine.locale fr_FR / cp1252 la locale n'est pas déterminable
machine.free_space 654.0 GB on D:\ le disque ne répond pas
dcs.detected yes / no jamais
dcs.version 2.9.29.27278 DCS absent, ou bannière introuvable dans dcs.log
dcs.variant stable, openbeta DCS absent
dcs.write_dir C:\Users\<user>\Saved Games\DCS DCS absent
dcs.log_age 3 d DCS absent
veaf.home C:\Users\<user>\.veaf le dossier ne peut pas être créé
veaf.log present, 1.2 MB / absent l'accès échoue
veaf.lua_modules 37 l'inventaire n'est pas lisible

Un champ inconnu vaut la chaîne unknown et jamais une chaîne vide : « absent » et « non collecté » se lisent pareil pour un humain, et un consommateur doit pouvoir les distinguer.

Règles d'évolution

  • Ajouter un champ est compatible : un lecteur lit ce qu'il connaît et ignore le reste. Le producteur fait déjà voyager un champ qu'il ne connaît pas.
  • Retirer ou renommer un champ ne l'est pas, et impose de passer à veaf-tools-doctor/2.
  • Un lecteur vérifie schema avant de parser. Un bloc portant un schéma inconnu est un bloc dont il ne peut rien supposer.

Un bloc reçu n'est pas une mesure

parse_block lit du texte arrivé par une issue publique ou un message Discord. N'importe qui peut en taper un à la main. Le producteur garantit la forme (un champ = une ligne), jamais la vérité d'une valeur : un consommateur qui agit sur tool.version traite une déclaration, pas une lecture prise sur la machine. C'est un point de confiance, pas un détail d'analyse syntaxique — FEAT-SUPPORT-BUG-INTAKE est le premier à passer la frontière.

Caviardage

Tout ce que produit doctor passe par veaf_libs.redaction.redact avant d'être affiché : nom de compte → <user>, adresse IP routable (v4 et v6) → <ip>, adresse e-mail → <email>, jeton ou mot de passe → <redacted>. Les adresses de bouclage sont conservées : elles ne portent rien de personnel et disent quelque chose d'utile.

Deux règles asymétriques, et l'asymétrie est délibérée.

L'identité est sur-caviardée. Le nom de compte est remplacé partout où il apparaît, pas seulement sous Users/ : il ressort dans un dossier temporaire (Temp\pytest-of-Prénom\), dans un nom de machine, dans un vidage de variables d'environnement. Mesuré sur 1489 enregistrements ERROR réels, il survivait 56 fois à un caviardage qui avait pourtant traité le chemin d'origine trois segments plus tôt.

Les identifiants ne sont pas caviardés. Il n'y a pas de règle d'entropie ici, et c'en est une décision : une première version remplaçait toute suite de 24 caractères ou plus mêlant lettres et chiffres. Mesurée sur le même journal, elle a produit 74 substitutions et zéro identifiant secret ; mesurée sur les fichiers de données du dépôt, elle capturait 169 GUID DCS et 493 autres identifiants, dont HVAR_USN_Mk28_Mod4_Corsair et M261_INBOARD_DE_M151_C_M274. Autrement dit unknown payload <redacted> : on garde « ça a planté » et on jette « sur quoi ». Un secret est donc reconnu à son contexte (une affectation à une clef de type token, password, access_token, client_secret…) ou à sa forme connue (préfixe de jeton GitHub, JWT, URL de webhook, identifiants dans une URL). Une suite aléatoire sans étiquette ni forme connue passe, volontairement.

Le caviardage se fait à la production, pas à la publication : au moment de publier, c'est un autre programme, sur une autre machine, et il est trop tard. Un consommateur peut repasser redact sur ce qu'il reçoit — l'opération est idempotente — mais il ne doit pas compter dessus pour rattraper une source non caviardée.