From 5ef618c2b4b4578ed477bfcb1d0bb19106fdbc47 Mon Sep 17 00:00:00 2001 From: "Thierry V." <46031203+thierryvm@users.noreply.github.com> Date: Wed, 30 Sep 2026 00:33:37 +0200 Subject: [PATCH] docs(agents): cover multi-step exercises in the audit agents What the agents reported about their own blind spots during #403/#404: - terminal-fidelity-auditor: rebuild a Git setup by hand (recipe) instead of stopping at NOT-RUNNABLE-HERE; replay the commands exercise steps ask for and check the claims of warn/restart/successMessage; git is not on pwsh's PATH; never launch a file by its association. - curriculum-validator: count validators used through stepAccepts (no false orphans), check steps (validate xor steps, per-step ByEnv symmetry, one solution command per step), unlocks as info, write scripts to a .mts file. - test-runner: a Playwright test.skip(true, reason) is a runtime skip, not a leaked .skip. - CLAUDE.md: the fidelity trigger covers step texts; multi-step exercises must have no dead end out of order. All agents keep their model alias (opus / sonnet), so the Sonnet agents already run on the latest Sonnet. Co-Authored-By: Claude Opus 5.5 --- .claude/agents/curriculum-validator.md | 11 +++++++++-- .claude/agents/terminal-fidelity-auditor.md | 8 ++++++-- .claude/agents/test-runner.md | 4 +++- CLAUDE.md | 3 ++- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/.claude/agents/curriculum-validator.md b/.claude/agents/curriculum-validator.md index 49b75dc..f153b9b 100644 --- a/.claude/agents/curriculum-validator.md +++ b/.claude/agents/curriculum-validator.md @@ -1,6 +1,6 @@ --- name: curriculum-validator -description: Validate curriculum.ts structure before any modification — env coverage via the *ByEnv fields, duplicate lesson or module IDs, prerequisites chain integrity, validator import/export sync, orphan validators, lesson setups resolved in lessonSetup.ts, LESSON_SOLUTIONS coverage of every exercise, and module completeness. Counts come from executed code, never by hand. Auto-invoked before adding or modifying lessons or modules. +description: Validate curriculum.ts structure before any modification — env coverage via the *ByEnv fields, duplicate lesson or module IDs, prerequisites chain integrity, validator import/export sync, orphan validators, lesson setups resolved in lessonSetup.ts, LESSON_SOLUTIONS coverage of every exercise, and module completeness. Counts come from executed code, never by hand. Auto-invoked before adding or modifying lessons or modules, and before changing the Exercise type, exerciseSteps.ts, lessonSetup.ts, lessonSolutions.ts or LessonPage.tsx. tools: Read, Grep, Glob, Bash model: sonnet --- @@ -30,7 +30,8 @@ Si la commande échoue, écrire « compte non vérifié » — pas de nombre app node -e " const fs=require('fs');const c=fs.readFileSync('src/app/data/curriculum.ts','utf8'),v=fs.readFileSync('src/app/data/validators.ts','utf8'); const imp=new Set((c.match(/import\s*\{([^}]*)\}\s*from\s*'\.\/validators'/)?.[1]??'').split(',').map(s=>s.trim()).filter(s=>/^validate\w+$/.test(s))); - const ref=new Set([...c.matchAll(/validate:\s*(validate\w+)/g)].map(m=>m[1])); + // A multi-step exercise uses its validators inside steps: stepAccepts(validateX, ctx). + const ref=new Set([...c.matchAll(/validate:\s*(validate\w+)/g),...c.matchAll(/stepAccepts\(\s*(validate\w+)/g)].map(m=>m[1])); const exp=new Set([...v.matchAll(/^export\s+(?:const|function)\s+(validate\w+)/gm)].map(m=>m[1])); const d=(a,b)=>[...a].filter(x=>!b.has(x)); console.log({exported:exp.size,imported:imp.size,referenced:ref.size,refNotImported:d(ref,imp),impNotExported:d(imp,exp),orphans:d(exp,ref)});" @@ -60,6 +61,12 @@ Si la commande échoue, écrire « compte non vérifié » — pas de nombre app 8. **Tests des validateurs** : chaque validateur a un `describe` dans `validators.test.ts`. 9. **Leçons sans bloc `code`** : uniquement `text` / `info` / `tip` / `warning` = WARNING (apprentissage dégradé). 10. **Completeness** : module sans leçons, leçon sans exercice, exercice sans `successMessage` = WARNING. +11. **Exercices en étapes** (depuis #403, `src/app/data/exerciseSteps.ts`) : un exercice a **soit** `validate`, **soit** `steps` (jamais les deux, le type l'impose). Pour chaque étape : `instruction`, `hint` et `check` présents ; `instructionByEnv` et `hintByEnv` symétriques (une variante Windows de l'une sans l'autre = WARNING). Dans `LESSON_SOLUTIONS`, un exercice en étapes liste **une commande par étape**, dans l'ordre (`lessonFidelity.test.ts` le vérifie). Les `check` lisent l'état du terminal : un `check` qui serait déjà vrai dans l'état du `setup` ferait sauter l'étape (WARNING à signaler, la preuve relève des tests). +12. **`unlocks`** : un ID absent du curriculum (module futur) = INFO seulement, sauf si du code UI le suppose existant. + +### Scripts + +Les one-liners `node -e` / `npx tsx -e` avec guillemets imbriqués cassent sous Git Bash. Au-delà d'une ligne simple, écrire un fichier `.mts` temporaire (hors de `src/`), l'exécuter avec `npx tsx`, puis le supprimer. Le champ des blocs d'une leçon s'appelle `blocks` (pas `content`). ## Format de rapport obligatoire diff --git a/.claude/agents/terminal-fidelity-auditor.md b/.claude/agents/terminal-fidelity-auditor.md index 5424643..6f52648 100644 --- a/.claude/agents/terminal-fidelity-auditor.md +++ b/.claude/agents/terminal-fidelity-auditor.md @@ -1,6 +1,6 @@ --- name: terminal-fidelity-auditor -description: Compare the terminal SIMULATOR with a REAL shell — runs the same commands in the engine (terminalEngine.ts plus commands/*.ts) and in GNU bash or PowerShell 7 inside a throwaway sandbox that mirrors createInitialState, then classifies each command (MATCH, COSMETIC, ENGINE-WRONG, THEORY-WRONG, NOT-SIMULATED, NOT-RUNNABLE-HERE) and proposes fixes whose expected values come from the real shell. Run after any change to terminalEngine.ts, commands/*.ts or lesson code blocks in curriculum.ts, before a release, or on demand. Complementary to content-auditor and the lessonTheory ratchet, which compare lessons to the engine, never the engine to reality. +description: Compare the terminal SIMULATOR with a REAL shell — runs the same commands in the engine (terminalEngine.ts plus commands/*.ts) and in GNU bash or PowerShell 7 inside a throwaway sandbox that mirrors createInitialState, then classifies each command (MATCH, COSMETIC, ENGINE-WRONG, THEORY-WRONG, NOT-SIMULATED, NOT-RUNNABLE-HERE) and proposes fixes whose expected values come from the real shell. Run after any change to terminalEngine.ts, commands/*.ts, lesson code blocks in curriculum.ts, or exercise step texts (steps, warn, restart, successMessage) that describe a shell's behaviour, before a release, or on demand. Complementary to content-auditor and the lessonTheory ratchet, which compare lessons to the engine, never the engine to reality. tools: Bash, Read, Grep, Glob model: sonnet --- @@ -87,7 +87,11 @@ Le script donne une classe mécanique ; **tu la relis** avant de la rapporter (e - **Git Bash ≠ Linux pour les métadonnées** : `ls -l` montre un nombre de liens, un groupe numérique, une taille de dossier 0 et des bits `x` émulés (NTFS). Écarts sur ces colonnes = `NOT-RUNNABLE-HERE` (ou WSL). - **Sortie non-TTY** : `ls` seul imprime une entrée par ligne quand stdout n'est pas un terminal ; le moteur imite l'affichage interactif en colonnes. Classer `COSMETIC`. -- **Git** : si le `setup` de la leçon prépare un dépôt (commits, branches), le miroir ne le recrée pas → les commandes `git` sont `NOT-RUNNABLE-HERE`. Sans `setup` Git, `git init/status/add/commit` locaux tournent dans le bac à sable. +- **Git** : si le `setup` de la leçon prépare un dépôt (commits, branches), le script ne le recrée pas. Ne pas s'arrêter à `NOT-RUNNABLE-HERE` : ce sont les leçons Git qui comptent le plus. Recréer le dépôt **à la main** dans un `mktemp -d`, fichiers et commits identiques au `setup` (lire `lessonSetup.ts`), avec cet environnement : `GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_NOSYSTEM=1 GIT_AUTHOR_NAME=user GIT_AUTHOR_EMAIL=user@terminal-lab.local GIT_COMMITTER_NAME=user GIT_COMMITTER_EMAIL=user@terminal-lab.local GIT_EDITOR=true EDITOR=true VISUAL=true GIT_PAGER=cat PAGER=cat GIT_MERGE_AUTOEDIT=no HOME=$(mktemp -d) GNUPGHOME=$(mktemp -d)`, puis `git -c init.defaultBranch=main init` et `git config core.autocrlf false`. Jamais de signature, jamais le vrai `~/.gitconfig`. Les identifiants de commit diffèrent forcément : les normaliser. Le moteur, lui, se rejoue avec `processCommand` après `setup.apply(createInitialState(), env)`. Sans `setup` Git, `git init/status/add/commit` locaux tournent dans le bac à sable du script. +- **Git sortie non-TTY** : un vrai `git log` redirigé ne décore pas (`(HEAD -> main)`), le moteur décore comme un terminal : `COSMETIC`. +- **Exercices en étapes** (`exercise.steps`, depuis #403) : le script ne lit pas les étapes. Pour chaque commande qu'une étape demande de taper (texte entre accents graves de `instruction` / `instructionByEnv`), rejouer la séquence complète moteur et vrai shell comme ci-dessus. Vérifier aussi les affirmations des textes `warn`, `restart` et `successMessage` (« Git refuse », « n'affiche plus rien », « Fast-forward »…) : un texte qui décrit un comportement du shell est une théorie comme une autre, et un texte sans `…ByEnv` s'affiche aussi sous Windows (ex. « `ls -a` montre `.git` » est faux en PowerShell). +- **PowerShell et git** : `git` n'est pas toujours dans le PATH de `pwsh -NoProfile`. Rejouer les commandes `git` avec Git Bash, et ne demander à pwsh que les cmdlets. +- **Pas d'application externe** : ne jamais lancer `Invoke-Item`, `Start-Process`, ni un fichier par son association (`.\script.sh` sous PowerShell). Raisonner à partir de `Get-Command` et de la documentation, et dire que c'est supposé. - **macOS** : aucune vérification possible ici. ## Script diff --git a/.claude/agents/test-runner.md b/.claude/agents/test-runner.md index db74c8b..af5ef84 100644 --- a/.claude/agents/test-runner.md +++ b/.claude/agents/test-runner.md @@ -82,10 +82,12 @@ Détecter les `.only(` et `.skip(` **commités** dans les fichiers de tests — ```bash grep -rnE "\b(it|describe|test|suite)\.(only|skip)\(" src/test/ e2e/ 2>/dev/null \ - | grep -vE ":\s*(//|/\*|\*\s|\*$)" + | grep -vE ":\s*(//|/\*|\*\s|\*$)" \ + | grep -vE "test\.skip\(true, " ``` - Le motif ne capture pas `it.skipIf(...)` (skip conditionnel légitime des tests d'intégration) ni `it.fails` (cliquet `KNOWN_DESYNCS` de `lessonFidelity.test.ts`). +- Il écarte aussi `test.skip(true, '')` : c'est un skip Playwright décidé **à l'exécution** avec sa raison (ex. `e2e/desktop/touch-targets-preserve.chromium.spec.ts`, `e2e/mobile/touch-targets.webkit.spec.ts`, bouton rendu seulement après consentement), pas un `.skip(` oublié. - Toute occurrence restante = CRITICAL : `file:line — .only/.skip leaked, test suite biased`. ## Étape 6 — Cliquets THI-353 : « ratchet grew » (CRITICAL) diff --git a/CLAUDE.md b/CLAUDE.md index adc77cf..5382db6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -168,7 +168,8 @@ App pédagogique pour apprendre le terminal. Bénévole, open source, 100% gratu ### Après chaque modification de `curriculum.ts`, `terminalEngine.ts` ou `commands/*.ts` - Invoquer l'agent **`test-runner`** → si VERDICT = ❌ Fix required, corriger avant de proposer un commit -- Si une sortie de terminal (moteur ou exemple de leçon) change : invoquer **`terminal-fidelity-auditor`** sur les commandes touchées. Un attendu de test vient du vrai shell, jamais de la sortie du moteur. +- Si une sortie de terminal (moteur ou exemple de leçon) change : invoquer **`terminal-fidelity-auditor`** sur les commandes touchées. Un attendu de test vient du vrai shell, jamais de la sortie du moteur. Idem quand un exercice en étapes (`steps`, `warn`, `restart`, `successMessage`) fait taper une commande ou décrit un comportement du shell (depuis #403). +- Exercice en étapes : aucune impasse. Une étape d'observation passe dès que l'élève observe, une étape d'action passe aussi quand son effet est déjà là, et toute impasse restante a un `warn` qui dit quoi faire (leçon de #404). `feature-dev:code-reviewer` doit chercher les séquences dans le désordre. - Les cliquets `KNOWN_THEORY_GAPS` / `BASH_SHOWN_ON_WINDOWS_MAX` (`src/test/lessonTheoryGaps.ts`) et `KNOWN_DESYNCS` (`lessonFidelity.test.ts`) ne peuvent que baisser. Après une correction, `npm run theory:gaps` régénère la liste. Le script refuse d'ajouter un écart ou de relever le compteur Windows : l'option `--allow-new` est réservée à un correctif déjà planifié. ### Incohérences Linear à corriger dès détection