Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions REATTESTATION.fr.md
Original file line number Diff line number Diff line change
@@ -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 <id-du-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 <pid-du-validateur> -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.
105 changes: 105 additions & 0 deletions REATTESTATION.md
Original file line number Diff line number Diff line change
@@ -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 <lab-id>
```

**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 <validator-pid> -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.
22 changes: 11 additions & 11 deletions validation-labs.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -124,7 +124,7 @@
"status": "failed",
"total": 5
},
"date": "2026-10-17",
"date": "2026-10-19",
"duree_s": 11,
"ecarts": [],
"etapes": [
Expand Down Expand Up @@ -180,7 +180,7 @@
"status": "failed",
"total": 5
},
"date": "2026-10-17",
"date": "2026-10-19",
"duree_s": 3,
"ecarts": [],
"etapes": [
Expand Down Expand Up @@ -236,7 +236,7 @@
"status": "failed",
"total": 5
},
"date": "2026-10-17",
"date": "2026-10-19",
"duree_s": 9,
"ecarts": [],
"etapes": [
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
Loading