From 139d91f57d60eed5564fc2a7148abd5f54681d1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20ROBERT?= Date: Mon, 19 Oct 2026 17:28:26 +0200 Subject: [PATCH] docs(reattestation): ce que ce catalogue exige, et ce qui y fait un faux verdict MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `REATTESTATION.md` et sa version française, écrites avec des valeurs mesurées sur le disque et non recopiées : labs et leur répartition shell/vm, hôtes et réseau attendus, fichiers de solution chiffrés, présence du secret, durée du dernier cycle, dates des verdicts, version du moteur déclarée. La page existe à cause de ce qu'une campagne a coûté ce soir. Elle a inscrit douze faux ROUGE dans un catalogue avant d'être arrêtée, et s'est bloquée vingt-trois minutes dans un autre. Aucune des deux causes ne tenait aux labs : un `.venv` dont les shebangs désignaient l'ancien nom du dépôt après son renommage, et un outil qui attendait un réseau que le bac à sable ne donne qu'à la commande de premier niveau. Dans les deux cas le validateur avait l'air de mesurer les labs alors qu'il mesurait sa propre panne, et il l'écrivait dans le relevé. Un faux ROUGE coûte plus cher qu'une absence de verdict : il envoie chercher un défaut dans un lab qui n'en a pas. La page porte donc quatre pièges, leur symptôme et leur correctif, puis la CAUSE de la péremption des verdicts de ce catalogue : un seul commit, vérifié par `git show` et non repris d'une liste, de la campagne du 2026-10-17 qui a rendu bilingues 289 fichiers de tests sur huit catalogues. Les intitulés de ces commits nomment ce qui rend le rejeu obligatoire plutôt que souhaitable : des labs qui notaient faux, une contrainte qu'aucun test ne tenait, un helper masqué, des appels qui levaient au lieu d'afficher. Elle dit enfin ce que le format du relevé permet au site d'en dire. Son générateur ne lit `genere_le` et `dsoxlab` que si la clé `labs` est un dictionnaire : un relevé plat ne lui apprend rien, même s'il portait la version. Six relevés sur douze sont dans ce cas, et le site affiche alors une attestation sans pouvoir dire avec quoi elle a été obtenue. Co-Authored-By: Claude Opus 5 --- REATTESTATION.fr.md | 107 +++++++++++++++++++++++++++++++++++++++++++ REATTESTATION.md | 105 ++++++++++++++++++++++++++++++++++++++++++ validation-labs.json | 22 ++++----- 3 files changed, 223 insertions(+), 11 deletions(-) create mode 100644 REATTESTATION.fr.md create mode 100644 REATTESTATION.md diff --git a/REATTESTATION.fr.md b/REATTESTATION.fr.md new file mode 100644 index 0000000..33bbfaa --- /dev/null +++ b/REATTESTATION.fr.md @@ -0,0 +1,107 @@ +# Réattester les labs de ce catalogue + +> Page écrite le 2026-10-19, avec des valeurs **mesurées sur le disque** et non +> recopiées. Les chiffres vieillissent : le `validation-labs.json` reste la +> source de vérité pour les verdicts. + +## Pourquoi cette page existe + +Le 2026-10-19, une campagne de réattestation a inscrit **douze faux ROUGE** dans +un catalogue avant d'être arrêtée, et s'est bloquée vingt-trois minutes dans un +autre. Aucune des deux causes ne tenait aux labs : un `.venv` dont les shebangs +désignaient l'ancien nom du dépôt après son renommage, et un outil en ligne de +commande qui attendait un réseau que le bac à sable ne lui donnait pas. + +Dans les deux cas, le validateur **avait l'air de mesurer les labs alors qu'il +mesurait sa propre panne**, et il l'écrivait dans le relevé. Un faux ROUGE coûte +plus cher qu'une absence de verdict : il envoie chercher un défaut dans un lab +qui n'en a pas. + +## Ce que ce catalogue exige + +- **dsoxlab 0.5.4 ou plus** dans le `PATH`. C'est la version du moteur qui sera inscrite dans le relevé, et le relevé la déclare pour TOUS les labs : voir la section sur le rejeu partiel. +- Les outils épinglés par `mise.toml`, donc la commande se lance par `mise exec --`. Sans lui, un outil absent ou d'une autre version fait échouer des labs sans rapport avec leur sujet. +- Aucun secret : ce catalogue déclare `solutions: plaintext` dans son `meta.yml`, avec sa raison. Ses solutions servent de fixtures au lab suivant, donc elles doivent rester lisibles. + +## La commande + +```bash +mise exec -- uv run scripts/valider-labs.py +``` + +Un seul lab, pour éprouver le harnais avant d'engager les 7 : + +```bash +mise exec -- uv run scripts/valider-labs.py --lab +``` + +**Commencer par un seul lab n'est pas une précaution de principe.** C'est ce qui +distingue un défaut d'environnement d'un défaut de lab : si le lab unitaire +passe, le harnais est sain ; s'il échoue comme les autres, c'est le harnais +qu'il faut réparer. + +## L'état actuel + +Relevé actuel : 7 VALIDE. Moteur déclaré : **aucune version de moteur enregistrée**. Dates des verdicts : 2026-10-19. Durée cumulée du dernier cycle mesuré : **1 minutes** pour 7 labs, hors provisionnement et destruction d'infrastructure. + +## Les quatre pièges qui produisent un faux verdict + +**1. Un `.venv` au shebang mort.** Si le dépôt a été renommé, les scripts de +`.venv/bin/` pointent encore l'ancien chemin et ne s'exécutent plus. Le +symptôme est un échec identique sur chaque lab, souvent sur le déchiffrement de +la solution. Vérification et correctif : + +```bash +head -1 .venv/bin/ansible-vault # le chemin doit exister +uv venv --clear && uv sync # le recréer +``` + +**2. `uv run --no-sync` sur un venv vide.** Le drapeau suppose un environnement +déjà peuplé. Sur un clone neuf, il rend un `ModuleNotFoundError` avant d'avoir +regardé un seul lab. Lancer sans `--no-sync` la première fois. + +**3. Un outil qui attend un réseau que le bac à sable ne donne pas.** Mesuré +sur un `octl --version` resté bloqué vingt-trois minutes : seule la commande de +premier niveau est exemptée, un sous-processus ne l'est pas. Si une campagne +n'avance plus, regarder ses processus enfants avant de soupçonner un lab : + +```bash +ps -eo pid,etimes,cmd | grep -E 'python.*valider[-]labs' +ps --ppid -o pid,etimes,cmd +``` + +**4. Un rejeu partiel rend l'en-tête du relevé faux.** La version du moteur est +enregistrée **une fois pour tout le fichier**, alors que les verdicts sont +individuels. Rejouer un seul lab réécrit donc cet en-tête, et le relevé affirme +que tous ses labs ont été attestés avec la version courante. Soit on rejoue tout, +soit on restaure le relevé : + +```bash +git checkout -- validation-labs.json +``` + +## Ce que le relevé ne dit pas + +Il ne dit pas **avec quelle version du moteur chaque lab** a été attesté, mais +seulement celle de la dernière campagne. C'est un manque connu, et il se voit +dès qu'une campagne est partielle. + +**Ici, le relevé ne porte même pas de version de moteur** : il n'a aucune clé racine, là où six catalogues de la collection enregistrent `genere_le`, `dsoxlab` et la version de chaque outil employé. Tant que c'est le cas, rien ne permet de dire si ses verdicts valent pour le moteur courant. + +## Pourquoi les verdicts de ce catalogue sont périmés + +Un **seul** commit les a périmés, et il est vérifié plutôt que repris d'une liste : `ad1ce9d` du **2026-10-17**, « fix(labs): les messages d'échec en EN + FR (#7) », qui touche **8 fichiers de tests**. + +Ce n'est donc pas une dérive progressive : c'est une campagne coordonnée de bilinguisation des messages d'échec, menée le 2026-10-17 sur huit catalogues et 289 fichiers de tests. L'intitulé de ce commit nomme en outre un défaut corrigé au passage, et c'est lui qui rend le rejeu obligatoire : un test qui notait faux invalide le verdict qu'il a rendu. + +À ne pas confondre avec le commit du 2026-10-19, qui a changé l'hôte des `doc_url` de `blog.` à `www.`. Celui-là touche des descripteurs et de la documentation, il ne change aucun geste et ne périme rien. + +## Le format du relevé décide de ce que le site peut en dire + +Le relevé de ce catalogue est au format **plat**, et le site **n'y lit aucune version de moteur**, et affiche donc une attestation sans dire avec quoi elle a été obtenue. + +Le générateur du site ne lit `genere_le` et `dsoxlab` **que si la clé `labs` +est un dictionnaire** : un relevé dont les clés de premier niveau sont +directement les identifiants de labs part dans une autre branche, qui rend une +date et un outil vides. Corriger le champ sans corriger le format ne servirait +donc à rien, et c'est pour ça que les deux sont dits ensemble ici. diff --git a/REATTESTATION.md b/REATTESTATION.md new file mode 100644 index 0000000..62d647d --- /dev/null +++ b/REATTESTATION.md @@ -0,0 +1,105 @@ +# Re-attesting this catalogue's labs + +> Written on 2026-10-19, with values **measured on disk** rather than copied. +> Numbers age: `validation-labs.json` remains the source of truth for verdicts. + +## Why this page exists + +On 2026-10-19, a re-attestation campaign recorded **twelve false REDs** in one +catalogue before being stopped, and hung for twenty-three minutes in another. +Neither cause had anything to do with the labs: a `.venv` whose shebangs still +named the repository's former path after a rename, and a command-line tool +waiting on network access the sandbox does not grant. + +In both cases the validator **looked like it was measuring the labs when it was +measuring its own breakdown**, and it wrote that into the record. A false RED +costs more than a missing verdict: it sends people hunting for a defect in a lab +that has none. + +## What this catalogue requires + +- **dsoxlab 0.5.4 or newer** on the `PATH`. That is the engine version the record will carry, and the record states it for ALL labs: see the section on partial replays. +- The tools pinned by `mise.toml`, so run the command through `mise exec --`. Without it, a missing or differently versioned tool fails labs for reasons unrelated to their subject. +- No secret: this catalogue declares `solutions: plaintext` in its `meta.yml`, with its reason. Its solutions act as fixtures for the next lab, so they must stay readable. + +## The command + +```bash +mise exec -- uv run scripts/valider-labs.py +``` + +A single lab, to exercise the harness before committing to all 7: + +```bash +mise exec -- uv run scripts/valider-labs.py --lab +``` + +**Starting with one lab is not a matter of principle.** It is what separates an +environment defect from a lab defect: if the single lab passes, the harness is +sound; if it fails like the others, the harness is what needs fixing. + +## Current state + +Current record: 7 VALIDE. Declared engine: **no engine version recorded**. Verdict dates: 2026-10-19. Cumulative duration of the last measured cycle: **1 minutes** for 7 labs, excluding infrastructure provisioning and teardown. + +## The four traps that produce a false verdict + +**1. A `.venv` with dead shebangs.** If the repository was renamed, the scripts +in `.venv/bin/` still point at the old path and no longer execute. The symptom +is an identical failure on every lab, often on decrypting the solution. Check +and fix: + +```bash +head -1 .venv/bin/ansible-vault # the path must exist +uv venv --clear && uv sync # recreate it +``` + +**2. `uv run --no-sync` on an empty venv.** The flag assumes an already +populated environment. On a fresh clone it raises `ModuleNotFoundError` before +looking at a single lab. Run without `--no-sync` the first time. + +**3. A tool waiting on network access the sandbox does not grant.** Measured on +an `octl --version` that hung for twenty-three minutes: only the top-level +command is exempt, a subprocess is not. If a campaign stops progressing, look at +its child processes before suspecting a lab: + +```bash +ps -eo pid,etimes,cmd | grep -E 'python.*valider[-]labs' +ps --ppid -o pid,etimes,cmd +``` + +**4. A partial replay makes the record's header false.** The engine version is +recorded **once for the whole file**, while verdicts are per lab. Replaying a +single lab therefore rewrites that header, and the record then claims all its +labs were attested with the current version. Either replay everything, or +restore the record: + +```bash +git checkout -- validation-labs.json +``` + +## What the record does not say + +It does not say **which engine version attested each lab**, only the one from +the last campaign. This is a known gap, and it shows as soon as a campaign is +partial. + +**Here the record carries no engine version at all**: it has no root key, where six catalogues in the collection record `genere_le`, `dsoxlab` and each tool's version. Until that changes, nothing tells whether its verdicts hold for the current engine. + +## Why this catalogue's verdicts are stale + +A **single** commit made them stale, and it is verified rather than taken from a list: `ad1ce9d` of **2026-10-17**, "fix(labs): les messages d'échec en EN + FR (#7)", touching **8 test files**. + +So this is not gradual drift: it is a coordinated campaign to make failure messages bilingual, carried out on 2026-10-17 across eight catalogues and 289 test files. That commit's subject also names a defect fixed along the way, and that is what makes the replay mandatory: a test that graded wrongly invalidates the verdict it produced. + +Not to be confused with the 2026-10-19 commit, which moved the `doc_url` host from `blog.` to `www.`. That one touches descriptors and documentation, changes no action, and makes nothing stale. + +## The record's shape decides what the site can say about it + +This catalogue's record uses the **plat** shape, and the site **reads no engine version from it**, and therefore shows an attestation without saying what produced it. + +The site's generator reads `genere_le` and `dsoxlab` **only when the `labs` +key is a dictionary**: a record whose top-level keys are lab identifiers falls +into another branch, which returns an empty date and tool. Fixing the field +without fixing the shape would therefore change nothing, which is why both are +stated together here. diff --git a/validation-labs.json b/validation-labs.json index a0abcf1..416c29f 100644 --- a/validation-labs.json +++ b/validation-labs.json @@ -24,8 +24,8 @@ "status": "failed", "total": 5 }, - "date": "2026-10-17", - "duree_s": 4, + "date": "2026-10-19", + "duree_s": 5, "ecarts": [], "etapes": [ "état photographié, répertoire de travail absent, 6 conteneur(s) Docker présents", @@ -74,8 +74,8 @@ "status": "failed", "total": 5 }, - "date": "2026-10-17", - "duree_s": 18, + "date": "2026-10-19", + "duree_s": 21, "ecarts": [], "etapes": [ "état photographié, répertoire de travail absent, 6 conteneur(s) Docker présents", @@ -124,7 +124,7 @@ "status": "failed", "total": 5 }, - "date": "2026-10-17", + "date": "2026-10-19", "duree_s": 11, "ecarts": [], "etapes": [ @@ -180,7 +180,7 @@ "status": "failed", "total": 5 }, - "date": "2026-10-17", + "date": "2026-10-19", "duree_s": 3, "ecarts": [], "etapes": [ @@ -236,7 +236,7 @@ "status": "failed", "total": 5 }, - "date": "2026-10-17", + "date": "2026-10-19", "duree_s": 9, "ecarts": [], "etapes": [ @@ -292,8 +292,8 @@ "status": "failed", "total": 5 }, - "date": "2026-10-17", - "duree_s": 5, + "date": "2026-10-19", + "duree_s": 6, "ecarts": [], "etapes": [ "état photographié, répertoire de travail absent, 6 conteneur(s) Docker présents", @@ -348,8 +348,8 @@ "status": "failed", "total": 5 }, - "date": "2026-10-17", - "duree_s": 5, + "date": "2026-10-19", + "duree_s": 6, "ecarts": [], "etapes": [ "état photographié, répertoire de travail absent, 6 conteneur(s) Docker présents",