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
11 changes: 9 additions & 2 deletions .claude/agents/curriculum-validator.md
Original file line number Diff line number Diff line change
@@ -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
---
Expand Down Expand Up @@ -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)});"
Expand Down Expand Up @@ -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

Expand Down
8 changes: 6 additions & 2 deletions .claude/agents/terminal-fidelity-auditor.md
Original file line number Diff line number Diff line change
@@ -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
---
Expand Down Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion .claude/agents/test-runner.md
Original file line number Diff line number Diff line change
Expand Up @@ -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, '<raison>')` : 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)
Expand Down
3 changes: 2 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading