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",