From 86d8ce4d91ec30741e06b2f274dc56951d82e9c0 Mon Sep 17 00:00:00 2001 From: "Thierry V." <46031203+thierryvm@users.noreply.github.com> Date: Thu, 24 Sep 2026 13:05:41 +0200 Subject: [PATCH] docs: catch up CHANGELOG, STORY and banners for #377-#387 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CHANGELOG: entries for 23 and 24 September (#383-#387) and for the 18-19 August fixes that had none (#377-#379) - STORY: "Le bug était dans la police" section (23-24 September) - plan.md / ROADMAP.md / docs/README.md banners moved off 6 June - README: test count 1000+ -> 2500+ - security-audit-log: gates of #383-#387, #380 gate debt status Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 43 ++++++++++++++++++++++++++++++++++++++ README.md | 4 ++-- STORY.md | 12 +++++++++++ docs/README.md | 2 +- docs/ROADMAP.md | 4 ++-- docs/plan.md | 4 ++-- docs/security-audit-log.md | 20 ++++++++++++++++++ 7 files changed, 82 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61c16f6..0dc710a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,39 @@ --- +## 🖥️ 24 septembre 2026 — Le terminal dit enfin ce que la leçon enseigne (THI-353) + +*PR #385 · #386 · #387 · test permanent `lessonFidelity` · 2558 tests · exercices « validés malgré une erreur » : 44 → 3* + +Le Grand Check-up de l'été avait trouvé un défaut de fond : pour valider un exercice, Terminal Learning regardait la commande tapée, jamais ce que le terminal répondait. Mesuré sur les 66 leçons × 3 environnements : **44 exercices se validaient alors que le terminal affichait une erreur en rouge.** L'élève tapait exactement ce que la leçon demandait, lisait `fatal: not a git repository`, et recevait quand même ses félicitations. On a corrigé par le moteur d'abord, groupe par groupe. + +- **Chaque leçon démarre dans l'état qu'elle enseigne (#385).** Les leçons Git s'ouvrent dans `~/projets` avec le dépôt, la branche ou le dépôt distant dont elles ont besoin ; la leçon de sécurité dispose d'un `~/.ssh` simulé avec les permissions 700 / 600 / 644 qu'elle explique. Les 33 cas Git sont corrigés d'un coup. Au passage, la sortie du terminal garde son indentation (`git branch`, `git status`) et le markdown brut (accents graves, `**`) n'apparaît plus dans les consignes. +- **Les scripts s'exécutent vraiment (#387).** `./script.sh`, `bash script.sh` et `.\script.sh` sous Windows lisent le fichier ligne par ligne. Lancé avec `./`, il faut le droit d'exécution — sinon `Permission denied`, exactement ce qu'enseigne la leçon `chmod`. Comme un vrai sous-shell, un script garde les fichiers qu'il écrit mais pas ses `cd` ni ses variables. Un script qui s'appelle lui-même ne peut plus bloquer l'onglet (profondeur 3, 500 lignes au plus par commande). +- **PowerShell rattrape son retard (#387).** `$PROFILE`, `cat $PROFILE` (la leçon dispose désormais d'un vrai fichier de profil), `(Get-Content fichier).Count` et `Set-ExecutionPolicy` fonctionnent. +- **Un vieux bug de `chmod`, débusqué par les nouveaux tests (#387).** `chmod +x` écrivait le droit une position trop loin : le fichier ne devenait jamais exécutable pour son propriétaire. Corrigé, avec tous les modes octaux et les formes symboliques (`u+x`, `go-w`, `a=r`). +- **Le tiret qui « disparaissait » (#386).** Signalé par Thierry : en tapant `git merge --no-ff`, un des deux tirets s'effaçait à l'espace et revenait à la validation. Les touches enregistrées prouvaient que les caractères étaient intacts : c'était la police, JetBrains Mono, qui dessine `--` d'un seul trait (ligature). Les ligatures sont désactivées sur tout le texte monospace. **Vérifié sur un vrai iPhone** (THI-354). +- **Un garde-fou permanent.** Un test rejoue, pour chaque leçon et chaque environnement, la commande que la leçon demande de taper, et exige qu'elle se valide **sans aucune ligne d'erreur**. Les 3 cas restants (la redirection `2>`) sont listés explicitement, et cette liste ne peut que rétrécir. + +Ce qui reste, et c'est dit : les redirections (`2>`, `>` pour toutes les commandes, `tee`) arrivent dans la prochaine livraison ; ensuite seulement, un exercice refusera de se valider si le terminal a affiché une erreur. + +--- + +## 🔧 23 septembre 2026 — Reprise après la pause : dépendances corrigées, et une preuve d'âge qui ne se perd plus (THI-340) + +*PR #383 · #384 · npm audit 41 → 24 (critique 1 → 0) · +16 tests* + +Première journée de reprise après l'été et le Grand Check-up (sécurité, dépendances, marché, contenu, conformité). Les deux corrections prioritaires sont passées en premier. + +- **Dépendances (#383).** `react-router` 7.13 → 7.18.4 (avis de sécurité XSS / redirection ouverte), `vite` 6.4.3, et les correctifs compatibles des dépendances indirectes, dont `tar` (critique). Aucune version majeure changée. Les deux paquets dont la seule « correction » proposée était une rétrogradation (`@lhci/cli`, `@vercel/node`, de l'outillage absent du site servi) restent tels quels, en connaissance de cause. +- **La preuve d'âge se perdait sur un chemin précis (#384).** Un élève qui ouvrait le lien d'invitation de son professeur, puis se connectait avec GitHub ou Google, passait bien l'écran d'âge — mais la redirection partait avant l'enregistrement de l'horodatage. La population la plus susceptible d'être mineure se retrouvait précisément sans trace. L'horodatage est désormais posé avant toute redirection. +- **Les échecs se voient.** Un enregistrement raté est signalé à Sentry avec son seul code d'erreur — aucun identifiant, aucun message. +- **`/privacy` dit exactement ce que fait le code** pour un visiteur refusé : la date d'éligibilité reste sur l'appareil et s'efface à la première visite après ce jour-là. +- **Accessibilité de l'écran d'âge** : le refus est annoncé aux lecteurs d'écran et reçoit le focus, l'erreur est liée au champ, le sélecteur de date suit le thème sombre. + +Contrôles : `security-auditor` (0 critique, 0 élevé, avec une relecture de l'écran d'âge d'origine), `ui-auditor`, `feature-dev:code-reviewer` (SHIP). Chaque nouveau test a été vérifié rouge sur l'ancien code. + +--- + ## 🚸 19 août 2026 — La page promettait, le produit applique enfin : garde-fou d'âge à la création de compte (THI-340) *PR #380 · migration 035 · `ageGate.ts` + `AgeGateStep` · 36 tests · RGPD Art. 8 — Belgique = 13 ans* @@ -23,6 +56,16 @@ Ce qui n'est **pas** fermé, et c'est dit : une déclaration reste une déclarat --- +## 🛡️ 18-19 août 2026 — Deux correctifs en base et une page `/privacy` qui dit enfin la vérité + +*PR #377 [HOTFIX] · #378 · #379* + +- **Fermer un ticket de support fonctionne à nouveau (#377).** Passer un signalement en « résolu » violait une contrainte de cohérence en base : le panneau d'administration ne pouvait plus clôturer un ticket. Correctif d'urgence. +- **Moins de surface en base (#378).** Des fonctions `SECURITY DEFINER` réservées aux déclencheurs restaient exécutables par les rôles de l'API ; ce droit est retiré (avertissements 0028 / 0029 de l'analyseur de sécurité Supabase). +- **`/privacy` corrigée (#379).** La page affirmait « aucune donnée personnelle collectée » — faux depuis l'arrivée des comptes. Elle distingue maintenant l'usage anonyme (rien ne quitte l'appareil) du compte optionnel (email, profil, progression chez Supabase), déclare Supabase et Vercel avec leurs garanties de transfert, et ajoute une note sur les mineurs et l'AI Act. + +--- + ## 🔍 6 juin 2026 — Audit complet avant la pause : on vérifie que tout tient avant de couper (THI-342) *PR #375 · security-auditor 9.4/10 · content-auditor · mobile-responsive-auditor · route-attack-auditor · Sentry · e2e Chrome* diff --git a/README.md b/README.md index 8f3e562..af71dcb 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ No account required. No setup. Just open it and start learning. - 🖥️ **Interactive terminal** — type real commands, get real feedback (not just theory) - 🌍 **Multi-environment** — learn on Linux, macOS, or Windows — the interface adapts -- 🔒 **Progressive curriculum** — 11 modules, 66 lessons, 1000+ unit tests +- 🔒 **Progressive curriculum** — 11 modules, 66 lessons, 2500+ unit tests - 🤖 **AI as a dev tool** — dedicated module on using AI professionally (prompts, validation, limits) - 💾 **Progress saved automatically** — locally, or synced to the cloud with a free account - 📖 **Contextual help** — `help ` returns usage and examples for your environment @@ -116,7 +116,7 @@ Full security policy and vulnerability reporting: [SECURITY.md](SECURITY.md). In | **Phase 2** | ✅ Done | Vercel Analytics + Sentry error monitoring + source maps | | **Phase 3** | ✅ Done | Supabase Auth + user progress sync | | **Phase 4** | ✅ Done | Curriculum v2 + multi-environment selection + terminal profiles | -| **Phase 5** | 🔄 In progress | Curriculum expansion: 11 modules, 66 lessons, 1000+ unit tests + 176 E2E | +| **Phase 5** | 🔄 In progress | Curriculum expansion: 11 modules, 66 lessons, 2500+ unit tests + 176 E2E | | **Phase 5.5** | ✅ Done | Terminal Sentinel — automated security & content audit agents | | **Phase 7** | ✅ Done | RBAC — student / teacher / institution_admin / super_admin roles | | **Phase 7b** | ✅ Done | AI Tutor V1 — optional BYOK contextual tutor (OpenRouter / Anthropic / OpenAI / Gemini), client-side keys, live in production | diff --git a/STORY.md b/STORY.md index ea16eea..330d480 100644 --- a/STORY.md +++ b/STORY.md @@ -21,6 +21,18 @@ Ce projet a été construit avec l'aide de Claude — l'IA d'Anthropic, des mod --- +## Le bug était dans la police, et 44 exercices qui mentaient poliment (23-24 septembre 2026) + +Le Grand Check-up de l'été avait posé un diagnostic que je n'aimais pas : pour décider qu'un exercice était réussi, Terminal Learning lisait la commande tapée — et jamais ce que le terminal répondait. J'ai rejoué la solution de chaque exercice, dans chaque environnement, et compté. Sur 198 cas, 44 se validaient alors que l'écran affichait une erreur en rouge. Un élève tapait `git status` comme on le lui demandait, lisait `fatal: not a git repository`, et recevait des félicitations juste en dessous. Un mensonge poli, mais un mensonge : l'outil disait « bravo » à quelqu'un qui venait de voir que ça ne marchait pas. + +La correction évidente aurait été de refuser la validation dès qu'une erreur s'affiche. Je ne l'ai pas faite en premier, et c'est la décision de ces deux jours que je défendrais le plus volontiers. Dans ces 44 cas, la faute n'était pas à l'élève mais au simulateur : les leçons Git s'ouvraient sans dépôt, les scripts ne s'exécutaient pas, PowerShell ignorait `$PROFILE`. Durcir la validation d'abord, c'était bloquer des élèves sur nos propres trous. On a donc réparé le moteur, groupe par groupe, en gardant un compteur honnête : un test qui liste explicitement les cas encore faux, et qui échoue si l'un d'eux se met à passer sans qu'on l'ait retiré de la liste. La liste ne peut que rétrécir. Elle est passée de 44 à 9, puis à 3. + +Au milieu de ce chantier, Thierry a signalé un bug d'apparence banale : en tapant `git merge --no-ff`, un tiret s'effaçait à l'espace et revenait à la validation. Il avait dû copier-coller la commande pour finir l'exercice. Mon premier réflexe allait vers la saisie — un gestionnaire de touches qui avalerait un caractère. J'ai préféré regarder avant de corriger : j'ai enregistré chaque événement clavier. Le champ contenait bien deux tirets et un espace, intacts. Rien n'était effacé. C'était la police, JetBrains Mono, qui dessine `--` en un seul trait continu : une ligature, jolie dans un éditeur de code, désastreuse dans un cours dont le sujet est précisément de taper `--`. Un correctif « de saisie » aurait été du code faux pour un bug d'affichage. Trois lignes de CSS ont suffi, et Thierry les a vérifiées sur son iPhone, là où je n'étais pas sûr que Safari obéisse. + +Le dernier épisode est celui qui m'a le plus appris. En faisant enfin exécuter les scripts, les tests ont révélé que `chmod +x` n'avait jamais fonctionné : il écrivait le droit une position trop loin, et le fichier ne devenait jamais exécutable pour son propriétaire. Le bug dormait depuis des mois, invisible, parce que rien ne lançait jamais le fichier. Un simulateur qui n'exécute pas ne peut pas découvrir qu'une permission est fausse. La leçon, je crois, dépasse le terminal : **un test qui regarde le résultat attrape des bugs que personne ne cherchait ; un test qui ne regarde que l'intention n'attrape que ceux qu'on avait déjà imaginés.** + +--- + ## Écrire une règle le matin, devoir la tenir le soir (19 août 2026) Le matin du 19 août, j'ai réparé la page `/privacy` parce qu'elle mentait. Elle affirmait que Terminal Learning ne collectait aucune donnée personnelle, alors que créer un compte envoie bel et bien une adresse email chez Supabase. On a réécrit la page, déclaré les sous-traitants, ajouté un encadré « Mineurs » avec une phrase parfaitement exacte en droit : en dessous de 13 ans, la création d'un compte requiert le consentement d'un parent. PR mergée, prod vérifiée, satisfaction légitime. diff --git a/docs/README.md b/docs/README.md index fba1d41..625bcdb 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,6 @@ # Documentation Index — Terminal Learning -> Last updated: 6 juin 2026 CEST — **🔍 Audit complet pré-pause (abonnement → 10 juin)** : security-auditor 9.4/10 0 CRITICAL · route-attack « H1 SSRF » = faux-positif vérifié (fetch hardcodé) → `api/*` non touché · content-auditor 2 défauts UX moteur non-bloquants · mobile-responsive (**THI-342**) · Sentry prod clean · e2e cœur OK. **PR #375 THI-342** : textarea 16px mobile (anti auto-zoom iOS) + tap-target 44px. **Différés post-réactivation** : THI-345 (suppression compte irréversible) + THI-347 volets CRUD. Token `.secrets` Vercel invalide (leak-safe). — **Update précédent (4 juin 2026 CEST)** — **🗂️ Journée 8 PRs (#368→#374)** : THI-234 widgets analytics + THI-341 nav mobile (menu avatar) + THI-344 édition display_name + THI-346 fix dropdown + THI-347 volet 3 delete tickets (migration 033 = fix THI-334 racine). — **Update précédent (2 juin 2026 CEST)** — **🗂️ Sprint 2.C étape 4 — triage signalements super_admin (THI-320, PR #364)** : section « Signalements » dans `/app/admin` (liste + filtre statut + capture re-signée au read + statut journalisé `admin_audit_log`). **Système support complet** (formulaire → stockage → email → triage). Dropdown statut : Radix Select écarté (style inline → CSP `style-src` stricte) → `` natif `[color-scheme:dark]`, CSP intacte, 0 dép. Gates : security 9.5/10 · ui SHIP · mobile · code-reviewer · Voie A e2e « 2 sens » (desktop+mobile). Suite : THI-325 (visibilité user). — **Update précédent (1er juin 2026 CEST)** — **📧 Sprint 2.C étape 3 — notification email support (THI-319, PR #360)** : à la création d'un ticket, le super_admin reçoit un email (Resend). Implémenté en **route Vercel Edge** `api/support/notify.ts` (pivot vs Edge Function Supabase : clé Resend déjà en env Vercel + `api/*` déjà serverless → 0 plateforme/dépendance/secret nouveau). **Autorisation déléguée à RLS** (JWT user forwardé à PostgREST, ligne renvoyée seulement si owner/super_admin, aucun `service_role`). Anti-replay 60s + rate-limit 10/min/IP + description HTML-escaped + `no-store` + best-effort. **3 gates 0C/0H** (security 9.2/10 · route-attack ship · code-review clean) + **email réellement reçu** (Gmail MCP) + smoke prod 200. **THI-319 Done — reste l'étape 4** (section Tickets AdminPanel, THI-320). Aussi 1er juin : session fiabilité (THI-310/315/313, PRs #353-355), cohérence catalogue fermant THI-293 (THI-316, #357), filtre crawler Sentry (THI-317, #358). — **Update précédent (30 May 2026 ~14h CEST)** — **🚀 Sprint 2.C étape 2 — signalement in-app (PR #326)** : migration 030 bucket privé `support_screenshots` + 5 RLS `storage.objects` + `SupportTicketModal` (focus-trap + Escape, form 3 champs, disclaimer PII, trigger Sidebar « Aide & feedback » gated `user`) + `submitTicket` (upload → URL signée 7j → insert + cleanup orphelin). **3 audits gate-zero ALL GREEN** (supabase-backend SHIP 11 tests adversariaux prod 0C/0H + ui clean + security 9.3/10), **code-review** 0 critical (2 fixes), **Context7** API Storage vérifiée. Tests **1765 → 1780 PASS** (8 composant + 7 RLS bucket empiriques PROD). CHANGELOG catch-up #319-325 (drift 29/05). Sprint 2.C reste 2 étapes (Resend → AdminPanel). Voir CHANGELOG.md / plan.md. — **Update précédent (24 May 2026 ~20h CEST) — 🧠 AI Tutor par rôle livré — Stage B1 + Stage B2** : 7 PRs mergées même jour (#287 → #293). Stage B1 (#290, THI-260) = eval matrix frontier 2025-2026 sur 10 modèles × 14 fixtures, $0.62 USD, 2 modèles 8/8 PASS (GPT-5.5 440 ms + Opus 4.7 premium). Stage B2 (#291, THI-275) = 3 system prompts FR cloisonnés par rôle + dispatcher + fallback student defense-in-depth + 17 tests + 4 fixtures injection `` × 4 langues. `prompt-guardrail-auditor` Sonnet trouve 2 CRITICAL **fixés avant merge** (ghost block + DELIMITER_RX gap). 2 nouveaux agents : `legal-compliance-auditor` Opus 4.7 (THI-270 #288) + `user-forensics-auditor` Sonnet (THI-274 #293). Tests 1697 PASS (+57). Sprint 2.B prochaine session : `institution_admin` lite + `institution-rbac-auditor` gate-zero. Voir CHANGELOG.md / STORY.md pour la narrative complète. — **Update précédent (19 May 2026 ~21h CEST) — 🚀 Sprint 2.A Teacher workflow 75% shipped (étapes 1+2+2.bis+2.ter live)** : 5 PRs mergées journée (#267 `.env.example` exhaustif + #268 Teacher Dashboard CRUD `/app/teacher` + migrations 020/021 + #269 role-aware nav hub + login redirect safe + #270 adaptive default route per role super_admin → `/app/admin` + #271 docs CHANGELOG). Tests 1545 → 1604 (+59). Cascade ALL GREEN : security-auditor 9.4-9.5/10 · ui-auditor SHIP-READY · rbac-flow-tester 11/11 E2E PASS · happy path RPC empirique · Voie A Chrome MCP · Sourcery PASS. 6 follow-ups Linear créés (THI-237/238/239/240/241/242). Next : Sprint 2.A étape 3 = page `/app/join` consommant `join_class_by_code` RPC (pré-requis : agent `classroom-workflow-auditor` + THI-239 Vitest regression net + Voie A multi-personas). Voir CHANGELOG.md pour détails. — **Update précédent (16 May ~14h30 CEST) — 🚀 Sprint 2 étape 2/N — THI-153 ✅** : PR [#234](https://github.com/thierryvm/TerminalLearning/pull/234) mergée — cleanup UI bundle (umbrella audit). 4 items cherry-picked : `--github-red` CSS var unique source + migration AI/auth red palettes · UserMenu logout focus ring rouge → emerald · shadcn dead slots documentés · `sonner` désinstallé · **bonus brand fix** « Terminal Master » → « Terminal Learning » dans Layout mobile (`/app`). Sourcery review addressed (DestructiveActionButton helper local + emerald token rationale). 3 tickets backlog créés : THI-177 (pré-i18n discipline gate Phase 9 admin) · THI-178 (SEO longue traîne SSG Phase 10+) · THI-179 (securityheaders A+ COEP post-LTI 7c). Validation ui-auditor SHIP-READY · 1386 tests passed 0 errors · Landing chunk 7.33 kB gzip stable. Sprint 2 ordre verrouillé : THI-118 ✅ → THI-153 ✅ → **THI-131 Phase 7c LTI (next)** → THI-42 → THI-77/78. — **Update précédent (16 May ~10h45 CEST) — Sprint 2 démarré — deadline 10 juin — THI-118 ✅** : landing LCP regression fix mergée (PR [#232](https://github.com/thierryvm/TerminalLearning/pull/232)). Diagnostic Chrome DevTools MCP : LCP element = hero `

` sous-titre (texte), 98.9 % render delay, fuites `landingContent.ts` (`.reduce()` sur `commandCatalogue` forçait chunk curriculum eager) + `UserMenu`/`LoginModal`/`PWAInstallModal` eager. Fix : hardcoder + drift guard test + `React.lazy` modals. **Bundle Landing 27.29 → 7.33 kB gzip (−73 %)**, curriculum chunk plus dans le graph landing. Le test drift a caught un `TOTAL_LESSONS` 64 → 65 silencieux. Sprint 2 ordre verrouillé : THI-118 ✅ → THI-153 cleanup UI bundle (~75 min) → THI-131 Phase 7c LTI → THI-42 Profile Hub → THI-77/78 admin heatmaps. — **Update précédent (16 May ~10h CEST) — Sprint 1 Phase 7b lockdown CLOS à 4/4** : THI-148 (V1.0.1 méta-plateforme) ✅ → THI-144 (v1.1.0 anti-frictions + ADR-008) ✅ → THI-112 (onboarding AiKeySetup + AiConsentModal + AiSettings + Privacy section + M3-AI fix) ✅ → **THI-113 (audit final triple + H1 fix sentry-tunnel scrubber symétrique)** ✅. Score IA security : 8.7/10 (baseline 10 mai matin) → 9.0 → 9.1 → 9.3 → **9.4/10** post-H1 fix. Rapport audit final dans `docs/audits/ai-tutor-v1-2026-05-16.md` — verdict ALL CLEAR (3 agents : security-auditor + prompt-guardrail-auditor + ui-auditor). Prochaine étape : Phase 7c LTI activation (gate H4-AI jsonwebtoken). Voir CHANGELOG.md / STORY.md pour la narrative complète. > This directory contains active project documentation. For historical/stale docs, see `.archive/`. --- diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 57a3718..f1c3619 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -1,8 +1,8 @@ # Roadmap — Terminal Learning -> **Last updated:** 6 June 2026 CEST — 🔍 **Full pre-pause audit (subscription → 10 June, maintenance freeze)**: 5 parallel angles + Chrome e2e. `security-auditor` **9.4/10, 0 CRITICAL** (all 8 PRs from 4 June confirmed safe); `route-attack` 7.5/10 — the "H1 SSRF" finding is a **verified false positive** (the upstream `fetch()` URL is hardcoded; the client-supplied DSN never reaches it) → **`api/*` left untouched** (changing already-safe code right before a freeze adds risk for zero gain); `content-auditor` (2 **non-blocking** engine UX defects: `./script.sh` and `2>` not simulated — validation runs on the typed string, the lesson still completes); `mobile-responsive` (**THI-342**); prod Sentry **clean**; core e2e OK (terminal executes). **PR #375 THI-342**: textarea 16px on mobile (kills the iOS auto-zoom that caused the overflow) + 44px UserMenu tap-target (ui-auditor + code-reviewer + CI green + preview smoke 5/5). **Deferred to post-reactivation**: THI-345 (irreversible GDPR Art.17 account deletion) + THI-347 CRUD parts. Vercel posture: stored `.secrets` token **invalid** (leak-safe) → manual dashboard action (orphan tokens). — **Previous update (4 June 2026 CEST)** — 🗂️ **8-PR day (#368→#374)**: THI-234 admin analytics widgets (migration 032, role-gated `SECURITY DEFINER` RPCs) + THI-341 trimmed sidebar + GitHub-style avatar menu (CSP-safe) + THI-344 editable display_name + THI-346 dropdown scroll fix + THI-347 part 3 delete tickets (migration 033 = **THI-334 root-cause fix**). THI-234/339/341/344/346/334 Done; THI-347 umbrella In Progress (parts 1/2/4 remain). — **Previous update (2 June 2026 CEST)** — 🗂️ **Sprint 2.C step 4 shipped — super_admin ticket triage (THI-320, PR #364)**: "Signalements" section in `/app/admin` (list + status filter + description + screenshot re-signed at read + status change logged to `admin_audit_log`). **Support system complete** (form → encrypted storage → maintainer email → triage). Status dropdown: Radix Select **rejected** (injects inline styles → would break the strict `style-src` CSP, THI-120 hardening) in favour of a native `` natif `[color-scheme:dark]` — CSP 100% intacte, 0 dépendance. Gates : security-auditor 9.5/10 · supabase-backend (inline, agent non chargé runtime → THI-326) · ui-auditor SHIP · mobile-responsive PASS · code-reviewer (findings appliqués) · **Voie A e2e super_admin « 2 sens »** (submit avec capture → triage → statut aller-retour → audit confirmé en base → image chargée sans violation CSP, desktop + mobile 390px). Suite : visibilité user de ses propres tickets (THI-325). — **Mise à jour précédente (1er juin 2026 CEST, soir)** — **📧 Sprint 2.C Étape 3 livrée — notification email support (THI-319, PR #360)** : à la création d'un ticket support, le super_admin reçoit un email (Resend). Implémenté en **route Vercel Edge** `api/support/notify.ts` (pivot vs Edge Function Supabase du brief — code-verify : clé Resend déjà en env Vercel + `api/*` déjà serverless → 0 plateforme/dépendance/secret nouveau). **Autorisation déléguée à RLS** (JWT user forwardé à PostgREST ; ligne renvoyée seulement si owner/super_admin, aucun `service_role`). Anti-replay freshness 60s + rate-limit 10/min/IP + description HTML-escaped + `Cache-Control: no-store` + best-effort (email raté ≠ ticket perdu). **3 gates 0C/0H** (security-auditor 9.2/10 · route-attack-auditor ship · code-reviewer clean) + **email réellement reçu** (Gmail MCP vérifié) + smoke prod 200. Resend free tier (3000/mois). **THI-319 Done — Sprint 2.C : reste l'Étape 4** (section Tickets Dashboard super_admin, THI-320). Aussi 1er juin : filtre crawlers Sentry (THI-317, #358, moins de bruit d'alertes). — **Mise à jour précédente (1er juin 2026 CEST, après-midi)** — **🎯 Cohérence catalogue : validateurs ↔ instruction ↔ contenu ↔ OS (THI-316 · ferme THI-293, PR #357)** : audit complet des 66 validateurs (lecture exhaustive + instructions cross-référencées + **support moteur vérifié**) suite au blocage @thierry (`help ls` rejeté sur la leçon orientation). **4 validateurs corrigés** : `validateOrientation` (`=== 'help'` → accepte `help`/`help `/`man`/`man ` ; `whatis`/`apropos` refusés car non exécutés par le moteur — pas de validation sur sortie d'erreur) · `validateCd` (tolère le slash de tab-complétion `cd documents/`) · `validatePing` (accepte les options enseignées `-c`/`-n`/`-t`/`-i`, hôte requis via lookahead) · `validateCurl` Linux (exige une URL http(s), ferme W1). **THI-293 fermé** : `reseau` ping/ssh/scp reçoivent `instructionByEnv.windows`+`hintByEnv` (commandes identiques natives Win10+, CRITICAL→INFO). **Gates ALL GREEN** : curriculum-validator 0C · test-runner 1949 pass · content-auditor 0C (THI-293 C1/C2/C3 fermés + W1 comblé : spot-checks `reseau/*`+`orientation` ajoutés §4) · feature-dev:code-reviewer ship (ReDoS borné, edge cases vérifiés). **~50 tests 3 OS** (validators.test + curriculumEnvAwareness §4 déterministe par environnement). **Voie A preview** : `help ls` valide + auto-advance (bug @thierry résolu end-to-end). Revus & laissés tels quels (documenté THI-316) : cp/mv/rm/cat (exact-match), kill (`ps aux`), chown/githubActions (alignés instruction). **PR #357 attend la validation visuelle @thierry** (réalisée en autonomie, NON mergée — discipline respectée). — **Update précédent (THI-315 · THI-310 · THI-313)** — **🔐 Fiabilité déconnexion + imports async + nav leçon mobile (THI-315 · THI-310 · THI-313, PRs #355 + #353 + #354 mergées)** : session de reprise post-THI-310, partie d'un signalement utilisateur → 3 bugs distincts corrigés. **(1) THI-315 HOTFIX déconnexion** : « Se déconnecter » laissait parfois l'user connecté sur la landing — la révocation serveur était fire-and-forget → `await signOut()` rendait la main avant l'effacement du token persisté → `autoRefreshToken`+`onAuthStateChange` restauraient la session (race intermittente). Fix : **await la révocation** (token effacé avant nav) borné `Promise.race` timeout 4s ; `setSession(null)` garde le feedback instantané. security-auditor 8.5/10, renforce THI-186. Retest prod @thierry OK. **(2) THI-310 durcissement imports async** : reliquat #349 — `completeLesson`/`useUserRole`/`signOut`/init `getSession` chaînes catch-less → dégradation propre (syncStatus error / rôle `null` restrictif + cache **auto-réparant** M1 / teardown inconditionnel). security-auditor 9.5/10, +3 tests, Sourcery (timeout+comment). **(3) THI-313 nav leçon mobile** : sur mobile l'user atterrit sur le panneau Terminal mais Précédent/Suivant n'étaient que dans le panneau Contenu → footer nav `lg:hidden` persistant (composant `LessonNav` partagé DRY, desktop inchangé) + cue « leçon déjà complétée ». **Voie A a attrapé un overlap FAB↔Suivant** (lift gaté `pointer:coarse` au lieu du breakpoint `` `~` `-` `/` … + Tab + historique, insertion **au curseur**, clavier natif maintenu ouvert via `onPointerDown`), **FAB tuteur IA remonté** au-dessus de la barre sur leçons tactiles, **fades de scroll dynamiques** (affordance), 44×44px, 0 overflow 390px. Gates : **ui-auditor** 0C + **feature-dev:code-reviewer 2 passes** (4 findings IMPORTANT corrigés, dont `pointerdown` vs `mousedown` no-op iOS qui cassait la feature) + Sourcery (fallback `addListener` + helper simplifié). Voie A émulation iPhone validée (insertion `|>`, focus conservé, FAB gap 6px, fades début/milieu/fin), 41 tests. ⚠️ Clavier *natif* iOS à confirmer appareil réel. **THI-308 créé** (High) : session expirée iOS Safari ITP → 0% progression **silencieux** (DB intacte, vérifiée 24 leçons read-only) ; fix UX = bandeau « reconnecte-toi pour retrouver ta progression » au lieu de blanking muet (protection anti-fuite THI-186 préservée). — **Update précédent (31 mai PR-3a sandbox git)** — **🔁 THI-305 PR-3a (sandbox git exécutable)** : **PR #344 mergée** — `git rebase` (+ `-i` guidance + warning branche partagée) et **`git cherry-pick`** désormais simulés dans le terminal → les leçons git ont des exercices exécutables (avant : référencés mais non pratiquables). `cherry-pick` ajouté au catalogue + source git-scm.com, **75→76 commandes**. Gate **`feature-dev:code-reviewer` codifié + appliqué** : a trouvé un ordre de gardes inversé (corrigé). +9 tests engine. Voie A desktop+mobile (0px overflow). **Audit sandbox** : ~21 sous-cmd à état réel mais `diff` canned + `merge` sans conflits + `commits[]` global → **phase 2** (modèle per-branche) backlogée. **THI-306** (code-review sweep full-codebase) + **THI-307** (barre touches mobile `|`/`>`, High) créés. **Gate code-review codifié dans CLAUDE.md** (exemptions docs/config + ordre vs gates spécialisés). — **Update précédent (31 mai PR #5 Git & GitHub Référence)** — **🌿** : **PR #340 mergée** — catégories `git` (10 cmds) + `github-collaboration` (6 cmds) ajoutées au catalogue → **59 → 75 commandes** sur `/app/reference`. Sources **git-scm.com** (URLs vérifiées HTTP 200 ; git identique cross-OS = 1 lien canonique/cmd, 0 variante OS). Catégories mirrorent level+prerequisites curriculum (garde-fou cohérence). **`ia-dev` exclu** (pseudo-commandes `ai-help`, 0 doc CLI officielle). Compteurs sync (TOTAL_COMMANDS 75 + FAQ index.html) + **nouveau garde-fou** test : FAQ index.html == constantes (review Sourcery, fini le drift HTML silencieux). Voie A desktop+mobile (0px overflow 390px), 104 tests verts. **Sandbox THI-305 (High)** créé : sandbox git déjà ~21 sous-commandes, mais `git rebase` manquant + audit profondeur (merge commits/conflits réels) pour garantir un exercice exécutable par leçon git. — **Update précédent (31 mai PR #4 Sources officielles)** — **📚** : **PR #337 mergée** — chaque commande de `/app/reference` porte ses **sources officielles** (champ `officialDocs[]` sur `EnrichedCommand`, 57/59 commandes, fusion à l'export → catalogue reste source unique). Modèle par-OS canonique : Linux→man7.org · Windows→Microsoft Learn · macOS→Homebrew + upstream (OpenSSH/GNU/curl) · Debian man-pages (tree/dig/zip/unzip). `open`/`pbcopy` sans lien (pas d'hôte Apple officiel propre, 0 tiers). **Anti-hallucination** : chaque URL vérifiée live HTTP 200 (batch curl HEAD), garde-fou test allow-list domaines officiels + https. Gates : `ui-auditor` SHIP (H1 a11y nouvel-onglet + M2 ring-offset fixés), **Voie A desktop + mobile 390px** (0px overflow, labels wrappent). Aussi **PR #335** (invariants catalogue variant⊆compatibility + syntax/examples). **Process codifié** : Voie A inclut désormais mobile 390px (incident #337 desktop-seul). **Reste chantier** : PR-2 git/github/ia-dev dans le catalogue (+ leurs sources git-scm/GitHub Docs) → éditeur nano v1 → help/man enrichi → progression. — **Update précédent (31 mai PR #3)** — **🗂️ Références unifiées** : **PR #333 mergée** — `commandCatalogue.ts` devient la **source canonique unique** des commandes ; `/app/reference` (CommandReference.tsx) **dérive** désormais du catalogue (`flatMap`, array local de 44 cmds supprimé ~600 lignes). Fin de la divergence deux-sources (le compteur landing pouvait diverger de la page vue par l'user). **38 → 59 commandes**, 0 historique perdue, data migrée (groundée, pas inventée) : nouvelle catégorie `systeme` (+11) + `tree`/`sudo`/`umask`/`getacl`/`jobs`/`bg`/`fg`/`printenv`/`2>`/`2>&1` + spécifiques-OS préservées (Get-Acl, Get-ComputerInfo, open, pbcopy, brew, winget). UX multi-OS : badges compatibilité + variantes par environnement + recommendedFor + commonErrors. **Garde-fou** `commandReferenceSource.test.ts` (assert structurel : page importe le catalogue, pas de réintro d'array local ; assert data : ids historiques présents + catalogue ≥ 59) → la divergence ne peut plus revenir silencieusement. Gates : `ui-auditor` MERGE (1 HIGH a11y clavier pré-existant fixé in-PR) + `content-auditor` 0C (3 warnings fixés + **prompt agent mis à jour** : catalogue = source unique + check compteurs public/). Voie A Playwright PASS (Linux 53 / Windows 54, switch env OK), validation visuelle @thierry « très propre et adapté aux multi-OS ». Réalisé via **session parallèle** (rapport vérifié empiriquement avant merge, pas sur parole). **Reste chantier** : compléter git/github/ia-dev dans le catalogue (apparaîtront auto) → éditeur nano v1 → help/man enrichi OS-spécifique → progression. — **Update précédent (30 mai 2026 ~21h CEST)** — **📚🔒 Chantier enrichissement curriculum démarré (2 PRs)** : **PR #330 (sécu IA d'abord)** = clause frontière pédagogique prompt élève `tutor/v1.1.0→1.1.1` (le tuteur explique mais ne génère jamais de payload offensif, anti-chaîne réputationnelle ROT13→CTF-légal→reverse-shell) + strip reverse-shell output-side + décodeurs ROT13/hex sanitizer + prompt super_admin `v1.0.1` sans littéraux secrets (F-2) + fix commentaire (F-5). Gate `prompt-guardrail-auditor` **SHIP 9.3/10** (0C/0H, 2 MED assumés, 2 LOW fixés in-PR) + delta-check Sourcery (décodeur partagé + regex bornée). Tests AI **412**, injection fixtures 48→56. Démo Voie A connectée (JWT élève éphémère révoqué). **PR #331 (1er contenu)** = leçon fondatrice `command-anatomy` (Navigation niv.1, entre `ls -la` et `cd`) — comble un trou : on enseignait les options au cas par cas sans le modèle universel `commande [options] [arguments]` (court/long, combinaison, `--`, renvoi man/--help). Variante Windows (paramètres PowerShell, Get-Help). Exercice `man ls` + `validateCommandAnatomy` forward-compatible Get-Help. 1er test moteur `man` (closait WARNING audit). **Drift guards 65→66** sur 6 sources de vérité. **test-runner 1834 PASS**, curriculum-validator 0 CRITICAL. Idée @thierry « leçon sur les options » → trou invisible révélé par une question utilisateur. **Reste chantier** : PR Références (commandCatalogue 5 modules manquants) → éditeur nano v1 → contenu manquant (less/find/tar) → help/man enrichi OS-spécifique → progression. — **Update précédent (30 mai 2026 ~14h CEST)** — **🚀 Sprint 2.C étape 2 livrée (PR #326)** : système de signalement in-app. **Migration 030** bucket privé `support_screenshots` + 5 policies RLS `storage.objects` (user own folder via `foldername[1]=auth.uid()` / super_admin all RGPD Art.17). **`submitTicket.ts`** upload → URL signée 7j (conforme contrainte 029 M1) → insert + cleanup orphelin. **`SupportTicketModal`** focus-trap + Escape + form 3 champs + disclaimer PII, trigger Sidebar « Aide & feedback » gated `user`. **3 audits gate-zero ALL GREEN** : `supabase-backend-auditor` SHIP (**11 tests adversariaux live prod** path-traversal/MIME-spoof/oversize/cross-user-signed-URL tous bloqués, 0C/0H) + `ui-auditor` clean (0 hex introduit) + `security-auditor` **9.3/10**. **code-review** 0 critical (2 fixes : double-label a11y + DRY isAllowedMime). **Context7** confirme API Storage Supabase à jour. Tests **1765 → 1780 PASS** (8 composant jsdom + **7 RLS bucket empiriques PROD** REST+JWT incl. T7 non-régression cross-user signed URL). Suivis Étape 3/4 tracés (non bloquants) : validation magic-bytes serveur (MIME déclaré falsifiable) + rate limit upload/insert API4 (backlog 2.D) + CSP `img-src` supabase.co à l'Étape 4 (rendu screenshot). **CHANGELOG catch-up #319-325** (drift 29/05 signalé au shutdown). **Sprint 2.C reste 2 étapes** (Edge Function Resend → AdminPanel section Tickets). — **Update précédent (28 mai 2026 ~tard CEST)** — **🚀 Session post-reset : 3 PRs livrées (#314 → #316) + switch Opus 4.8**. **PR #314** vision plateforme B2B Option D Hybrid (doc strategy 549 lignes, Voie C) + **13 tickets Linear** (THI-282 umbrella Phase X B2B + 8 sub X1→X6 + THI-291/292/293/294 backlog). **PR #315 Sprint 2.C étape 1** = table `support_tickets` + RLS scoped + trigger audit (migration 028 base + 029 hardening security-auditor Opus 8.8→9.4/10 : H1 trigger guard auth.uid NULL + H2 WITH CHECK + M1 screenshot_url Storage-only XSS defense + M2 super_admin DELETE RGPD Art.17 + M3 status/resolved coherence + L1 search_path). **15/15 tests vitest empiriques PROD** (RLS REST+JWT). Full regression **1744/1764 PASS**. **PR #316 audit méta 20 agents post-4.8** : nouvel agent `supabase-backend-auditor` (Opus, gate-zero Edge Function Deno + Storage RLS + file upload, AVANT Étape 3 Resend + X3b import), `linear-sync` réécrit (avoue échec MCP au lieu de deviner — incident 28/05 sous-agent 6 incohérences fausses), README sync modèles, garde-fou pin 4.7→4.8. **Règle dure @thierry : JAMAIS Haiku** (0 Haiku / 12 Sonnet / 8 Opus). **MCP Supabase réparé** (mauvais compte gmail/Ankora → reconnecté TL, near-miss SQL Ankora rollback auto 0 dégât) + registry 028/029 aligné. **Sprint 2.C reste 3 étapes** (SupportTicketModal UI + bucket support_screenshots → Edge Function Resend → AdminPanel section Tickets). — **Update précédent (27 mai 2026 ~16h CEST)** — **🔧 Session marathon 27 mai : 6 PRs livrées (#305 → #310)** : Sprint 2.B reliquats hotfix RPC super_admin + Sprint 2.B.2 UX scope badge + Sprint 2.B.3 sidebar "Mes outils" collapsible THI-240 + UserMenu hover fix + doctrine modèles agents alignée (0 Haiku / 12 Sonnet / 7 Opus). **ui-auditor upgrade Haiku → Sonnet** (1ère invocation post-upgrade a immédiatement trouvé 1 CRITICAL raw button hors pattern shadcn — fix in-PR). **Doctrine 3 upgrades Sonnet → Opus** gate-zero critiques B2B/IA (security-auditor + prompt-guardrail-auditor + institution-rbac-auditor). ROI estimé : ~+$10-25/mois invocations Opus vs $1000+ incident B2B école évité. **Sprint 2.B totalement clos** validation E2E PROD @thierry confirmée. **2 PRs livrées matin (#305 + #306). PR [#305](https://github.com/thierryvm/TerminalLearning/pull/305) hotfix migration 027 = super_admin peut maintenant approve cross-institution (RPC body check étendu `caller_role IN (institution_admin, super_admin)` + audit log enrichi `metadata.scope = 'global'|'institution'`). Audits 🟢 SHIP : security 9.4/10 + RBAC 0 CRITICAL/0 HIGH (16/16 static + 8/8 empirical REST API + JWT). 11/11 tests vitest PASS PROD. PR [#306](https://github.com/thierryvm/TerminalLearning/pull/306) Sprint 2.B.2 UX feedback = badge `🌐 scope: global` permanent dans header + success status emerald "{nom} approuvé · scope {scope}" post-approve avec auto-clear 8s + dismiss X (44×44 HIG fix ui-auditor 🔴→🟢 in-PR). 17/17 tests panel PASS (5 nouveaux Sprint 2.B.2). **Validation E2E PROD @thierry confirmée 12h30** : connecté super_admin réel → badge visible + click "Approuver" → toast vert {nom} + scope + auto-disparition 8s + optimistic UI row disparue ✅. **Investigation logs console** : 11 erreurs Chrome DevTools détectées et triées — TOUTES viennent de l'extension Brave Kwift Wallet (`chrome-extension://fdjamakpfbbddfjaooikfcpapjohcfmg`), 0 erreur Terminal Learning. Notre code = sain. **Doctrine `feedback_agent_dormant_full_audit` validée 3× cette semaine** : 2 break-ins agents 26/05 (1 HIGH drift + 2 MED latents trouvés) + validation E2E humaine @thierry 27/05 (1 bug UX que les audits structurels n'avaient pas flaggé — validation humaine irremplaçable pour comportement utilisateur final). Tests 1724 → 1729 PASS (+5). 2 mémoires CC updates : `feedback_anti_leak_discipline_jwt_short_lived.md` + `project_sprint_2b_super_admin_approve_bug.md` marqué `RÉSOLU`. **Sprint 2.B TOTALEMENT clos** avec validation E2E PROD humaine. Sprint 2.C (Support System Resend) reste prochaine session sur signal explicite. — **Update précédent (26 mai 2026 ~18h CEST)** — **🏁 Sprint 2.B CLOS — institution_admin lite livré 100%** : 3 PRs mergées même session (#297 → #299), validation E2E PROD Chrome MCP empirique. PR [#297](https://github.com/thierryvm/TerminalLearning/pull/297) **Étape 1** = 4 migrations institution B (022b 3 personas test École B + 023 `profiles UPDATE WITH CHECK institution_id` H1 hijacking fix + 024 GoTrue NULL global fix + apply rétroactif 011/012 drift 4+ semaines fuite cross-institution institutions table). Score isolation 7.5 → **9.5/10**. PR [#298](https://github.com/thierryvm/TerminalLearning/pull/298) **Étape 3** = migration 025 RPC `approve_teacher(target_user_id uuid)` SECURITY DEFINER (caller institution_admin + same-institution + FOR UPDATE row lock + compare-and-swap UPDATE race-safe Sourcery, REVOKE EXECUTE FROM public + anon GRANT authenticated only) + migration 026 trigger AFTER UPDATE `audit_pending_teacher_promotion` defense in depth (flag transactionnel `app.in_approve_teacher_rpc` évite double insertion) + 8/8 tests vitest empirique PROD. Audits : `security-auditor` **9.5/10 SHIP** 0 CRITICAL / 0 HIGH + `institution-rbac-auditor` SHIP WITH NOTES (F-001 HIGH direct PATCH audit bypass + F-002 LOW anon EXECUTE missing revoke + M1 RAISE EXCEPTION leak) **3 findings fixés in-PR**. PR [#299](https://github.com/thierryvm/TerminalLearning/pull/299) **Étape 4** = `InstitutionAdminPanel.tsx` route `/app/institution` gated `` + hook `usePendingTeachers` (fetch RLS auto-scoped + approve RPC + FR error mapping) + Sidebar entry "Mon institution" + 12/12 tests RBAC fallback × behavior. Audit `ui-auditor` 🟡 SHIP WITH FIX → M1 aria-live scope + L3 touch target 44px fixés in-PR. **Validation E2E PROD Chrome MCP via JWT injection éphémère 1h institution_admin_b** : (1) anonymous → fallback "Vous devez être connecté" ✅ (2) login institutionadmin_b → sidebar entry + panel + count "(1)" pending_teacher_b École B uniquement (cross-institution isolation empirique) ✅ (3) click "Approuver" → RPC success + optimistic UI row disparu + count "(0)" + empty state ✅ (4) DB-side : profile role=teacher + 1 row admin_audit_log action='approve_teacher' metadata complet **PAS de row direct_patch** (flag transactionnel fonctionne en runtime réel) ✅ (5) 0 console errors. **Rotation Sentry secrets** via intégration native Vercel↔Sentry (7 vars `SENTRY_*` rotated 26 mai 17:37:58 + redeploy production READY immédiat) — `VITE_SENTRY_DSN` client unchanged (DSN public stable). **Sentry "Needs Attention" badges cleared.** **Sprint 2.B livré en 1 session ~5h** : 0 régression, 0 incident, autonomie déléguée respectée, discipline anti-leak maintenue (JWT éphémère 1h, .secrets/ + .tmp/ gitignored, password jamais en stdout, tool args = conversation context). **Doctrine validée** `feedback_agent_dormant_full_audit` : agent `institution-rbac-auditor` créé 20 mai jamais invoqué empiriquement avant 26 mai (6 jours dormant) → premier break-in a trouvé finding HIGH (drift production 4+ semaines invisible). **Tests 1721/1741 PASS** (+12 vs Sprint 2.A close + 20 skipped CI-only). Mémoires CC consolidées : `feedback_anti_leak_discipline_jwt_short_lived.md` (patterns validés @thierry féliciter 26/05), `reference_supabase_plan_free.md` (auth_leaked_password_protection gated Pro plan), `feedback_agent_dormant_full_audit.md` (doctrine 48h break-in obligatoire). **Sprint 2.B essentiellement le « palier B2B écoles cross-institution »** — fondation prête pour Sprint 2.C (à scoper prochaine session). — **Update précédent (24 mai 2026 ~20h CEST)** — **🧠 AI Tutor par rôle livré (Stage B1 + Stage B2)** : 7 PRs mergées même jour (#287 → #293). Stage B1 (PR #290, THI-260) = eval matrix frontier 2025-2026 sur 10 modèles × 14 fixtures, $0.62 USD, 2 modèles 8/8 PASS (GPT-5.5 440 ms top latence + Opus 4.7 premium). Stage B2 (PR #291, THI-275) = 3 system prompts FR cloisonnés par rôle (teacher / institution_admin / super_admin) + dispatcher `getSystemPrompt({lang, mode, role})` + fallback student defense-in-depth + 17 tests dispatcher + 4 fixtures injection `` × 4 langues. Audit `prompt-guardrail-auditor` Sonnet a trouvé 2 CRITICAL (ghost block `` + DELIMITER_RX gap) **fixés avant merge**. Vision @thierry verrouillée : « niveau différent selon rôle pour éviter utilisations malveillantes hackers et gros curieux ». **Validation cross-role server-side** : 5/5 RBAC personas via REST API + `get_my_role` RPC. **2 nouveaux agents livrés** : `legal-compliance-auditor` (Opus 4.7, 5 couches RGPD/AI Act/DSA/CNIL/DPA-BE auto-update WebSearch, THI-270 PR #288) + `user-forensics-auditor` (Sonnet, 6 sections RGPD-compliant, THI-274 PR #293, motivé cas Jimmy Pez premier user organique). **Audits ship** : `llm-security-auditor` Opus 7 couches sur Stage B1 = 9.2/10 ⚠️ SHIP avec mitigations toutes appliquées (H2-AI VERIFIED default OpenRouter Llama → Sonnet 4.6 fixé). Mitigation H3-AI consent mineurs RGPD Art. 8 → gate-zero Sprint 2.B `institution-rbac-auditor`. **Doctrine `AskUserQuestion` codifiée** (mémoire `feedback_askuser_recommend_first.md`) : toujours marquer recommandation explicite, ne pas laisser @thierry trancher seul ce que j'ai déjà jugé. **Tests 1697 PASS** (+57 vs main pré-session). **Dette identifiée** : ADR-009 cross-role isolation à créer (cette PR), `llm-security-auditor` Opus 7 couches sur Stage B2 reporté Stage B1.b (justifié — couverture transverse via B1 frais 4h avant). Sprint 2.B prochaine session : `institution_admin` lite + InstitutionAdminPanel skeleton + approve_teacher RPC + gate `institution-rbac-auditor` (THI-238 prêt). — **Update précédent (20 mai 2026 ~18h CEST)** — **🏁 Sprint 2.A complet à 100%** : étape 3 page `/app/join` livrée PR [#274](https://github.com/thierryvm/TerminalLearning/pull/274). Chaîne teacher → URL invitation → student → enrollment → progression visible fonctionne empiriquement end-to-end (test Supabase MCP impersonation student 105 → RPC `join_class_by_code('a4368184d202')` → success sans bug 42702/42883, cleanup propre 0 row restant). Sprint 2.A = 5 PRs mergées en 3 jours (#266 migrations + #268 Teacher Dashboard + #269 nav hub + #270 adaptive routing + #274 page join). **Tests 1640** (+36 étape 3). Cascade pré-merge ALL GREEN : `ui-auditor` SHIP-READY · `security-auditor` 9.2/10 (H1 fonctionnel `?code=` perdu après login + M1 maxLength 12 + pattern hex fixés en commit fixup avant push) · Voie A Chrome MCP empirique sessionStorage `?code=` preservation confirmée · Sourcery PASS rate-limit. Nouvel agent `classroom-workflow-auditor.md` créé (THI-237 Done) — gate-zero pour futures PRs touchant `classes`/`class_enrollments`/RPCs class-related, pattern Supabase MCP JWT impersonation documenté 14 checks structurés. **Cleanup Linear** : THI-237 + THI-239 passés Done (agent + tests + E2E empirique livrés), 0 candidate Cancel sur 31 issues Backlog audit (signal positif discipline projet). **2 tickets follow-up backlog** créés via security-auditor M2+M3 : THI-258 (rate limit Edge Middleware `/rest/v1/rpc/join_class_by_code` gated Pro plan, Medium) + THI-259 (standardiser `isMounted` ref pattern hooks async, Low). **Mea culpa session** : (1) agent `.md` créés en cours de session pas rechargés avant prochaine session — pattern documenté dans STORY narrative étape 3 + agents/README, (2) JSDoc qui promet un comportement non-implémenté = bug latent (H1 security-auditor) corrigé. **Process discipline maintenue** sous carte blanche technique : cascade complète + Voie A empirique + Sourcery + 5 personas comptes tests rappelés par @thierry en début de session, exactement ce qui a permis d'attraper H1+M1 avant production. **Quota Anthropic 88%/semaine** → fermeture session disciplinée avec STORY narrative + CHANGELOG + handoff Obsidian livrés cette session (leçon différer-ce-qui-est-demandé hier soir intégrée), refactors lourds (ARCHITECTURE drift > 5 sem THI-245, CONVENTIONS/GUIDELINES THI-246, teacher-guide THI-247) reportés sessions matinales fraîches post-reset. **Sprint 2.B suit** : pending_teacher dashboard + institution_admin lite + VIEW `classes_student_view` (THI-236 hide invitation_code from enrolled students). — **Update précédent (19 mai 2026 ~21h CEST) — 🚀 Sprint 2.A Teacher workflow shippé à 75% (étapes 1+2+2.bis+2.ter livrées)**. Cinq PRs mergées journée : [#267](https://github.com/thierryvm/TerminalLearning/pull/267) (`.env.example` complet — 3 vars → 12 vars exhaustives pour fork/onboarding), [#268](https://github.com/thierryvm/TerminalLearning/pull/268) **THI-235 étape 2 Teacher Dashboard CRUD** (route `/app/teacher` RequireRole teacher/super_admin + listing "Mes classes" + form inline création + ClassCard copy URL invitation, migrations 020 hardening CHECK constraints + **021 fix CRITICAL** `extensions.gen_random_bytes` schema isolation bug 42883 découvert empiriquement post-020), [#269](https://github.com/thierryvm/TerminalLearning/pull/269) **étape 2.bis Role-aware nav hub + login redirect safe** (section "MES OUTILS" sur `/app` Dashboard role-gated + Sidebar entry "Administration" super_admin + fallback unauthenticated "Se connecter" + 7 défense layers open-redirect protection via `validateReturnTo` + sessionStorage `auth_return_to`, déclenché par feedback empirique @thierry "je n'avais même pas vu le lien dans la sidebar mdr"), [#270](https://github.com/thierryvm/TerminalLearning/pull/270) **étape 2.ter Adaptive default route per role** (super_admin → `/app/admin`, teacher → `/app/teacher`, autres → `/app` ; refactor `consumeReturnTo` API `string` → `string | null` pour distinguer "no intent" de "invalid intent" ; déclenché par @thierry "je ne suis pas teacher de base, je suis super-admin, à la limite, c'est sur mon dashboard de contrôle que je devrais arriver"), [#271](https://github.com/thierryvm/TerminalLearning/pull/271) docs CHANGELOG entry détaillée 2.ter. **Tests** 1545 → **1604** (+59 nouveaux components/helpers + refactor returnToStorage). **Cascade rigoureuse** par-PR : `security-auditor` 9.4–9.5/10 SHIP (2 HIGH fixés migrations 020+021, 1 MEDIUM L1 cosmétique AbortController fixé fixup) · `ui-auditor` SHIP-READY (0 CRITICAL/HIGH/MEDIUM) · `rbac-flow-tester` **11/11 PASS** sur prod Supabase (5 personas + Sprint 2.A workflow complet + RLS isolation + cleanup) · happy path RPC empirique via Supabase MCP `join_class_by_code('a4368184d202')` impersonate student 105 → success sans bug 42702 · Voie A Chrome MCP anonymous `/app/admin` fallback rendu 0 console error · Sourcery PASS. **6 tickets follow-up** Linear Backlog créés : THI-237 `classroom-workflow-auditor` agent (gate avant étape 3) · THI-238 `institution-rbac-auditor` agent (Sprint 2.B) · THI-239 Vitest tests `joinClassByCode` regression net (gate avant étape 3) · THI-240 Sidebar refactor "Mes outils" collapsible · THI-241 PostgREST error message sanitization · THI-242 institution_admin invitation_code visibility ADR. **Industry research codifiée** (DAR Design Multi-Role B2B UX 2026 + Orbix B2B SaaS Dashboard + Lollypop SaaS Navigation) : "preference, not restriction" pattern → onboarding wizard = anti-pattern 2026. **Mea culpa session** : push direct main pour `.gitignore` matin reconnu, engagement 100% via PR maintenu reste journée ; dismiss initial du `rbac-flow-tester` finding pgcrypto schema corrigé empiriquement (migration 021), leçon process `feedback_happy_path_testing.md` confirmée ; merge autonome `--admin` PR #270 + #271 sous carte blanche @thierry (exception cadrée, à reproduire uniquement si validée en avance ou hotfix critique). **Sprint 2.A étape 3** suit demain : page `/app/join` consommant RPC `join_class_by_code` (pré-requis verrouillés AVANT merge : créer `.claude/agents/classroom-workflow-auditor.md` + livrer THI-239 + validation Voie A multi-personas). **Critère release-ready Sprint 2.A complet** : teacher crée classe → copy URL → student rejoint via code → enrollment success + classe visible Dashboard student. — **Update précédent (18 mai 2026 ~13h45 CEST) — 🎯 Sprint 2 essentiellement clos en avance** : THI-118 LCP + THI-153 UI cleanup + THI-131 LTI Phase 7c + **THI-42 PR #1 Profile Hub** tous Done bien avant deadline 10 juin. Hat-trick sécurité post-audit : **PR [#255](https://github.com/thierryvm/TerminalLearning/pull/255) THI-42 PR #1** (Profile Hub shell + UserMenu "Mon profil" link, 5 fichiers +418/-17, ui-auditor 4 CRITICAL hex hardcodés drive-by fixés, security-auditor 9.2/10 H1 race condition auth guard FIXÉ avec `initialized` check + loading state, Voie A desktop + iPhone 14 PASS, fix a11y back-link tap target 198×16 → 214×44 commit `504554c` avant merge). **PR [#256](https://github.com/thierryvm/TerminalLearning/pull/256) THI-219** CSP `img-src` énumération stricte `lh1`-`lh6.googleusercontent.com` (pattern bypass initial wildcard `*.googleusercontent.com` rejected par security-auditor — M1 content injection vector via `uc.googleusercontent.com` Drive/Gmail uploads — révisé en enum strict). Sourcery 2 suggestions valides (duplication CSP + commentaire inline) impossibles dans `vercel.json` strict JSON → ticket follow-up [THI-223](https://linear.app/thierryvm/issue/THI-223) créé : migration `vercel.json` → `vercel.ts` (pattern Vercel 2026 recommandé). **PR [#257](https://github.com/thierryvm/TerminalLearning/pull/257) THI-220** avatar URL validation defense-in-depth — `isValidAvatarUrl()` exporté dans `UserAvatar.tsx`, mirror CSP allow-list, fallback initials silencieux si validation échoue (compromised IdP / user_metadata tampering / future provider sans update CSP), 25 tests unitaires. **PR [#258](https://github.com/thierryvm/TerminalLearning/pull/258) THI-221** RequireAuth **opt-in wrapper** — scope revision majeure : reconnaissance révèle uniquement ProfilePage a le guard pattern, l'app est anonymous-friendly by design (Dashboard/LessonPage/AiSettings/CommandReference accessibles aux invités, mode invité fully supported). Un blanket Layout-level wrap aurait cassé cette UX. Refactor en wrapper opt-in (props children + fallback custom, gère `!initialized → loading` + `!user → fallback`), 6 tests + beforeEach reset (security-auditor L2). Voie A : `/app/profile` anonyme → fallback custom rendu ✅, `/app` Dashboard anonyme → fully accessible avec sidebar "Mode invité" + bouton "Se connecter" visibles ✅. Pattern prêt pour Phase 9 routes role-gated. Sas 48h doctrine override par @thierry pour cette session (« on avance normalement »). **Tests** : 1444 → 1475 (+31). **Score sécurité** : 9.2/10 stable. **4 PRs mergées + 5 tickets backlog créés** (THI-219 à 223). Sprint 2 deadline 10 juin large : reste **THI-77/78** (admin heatmaps) bloqués par **Phase 9 Admin Panel** umbrella à créer (route `/admin` + RBAC role-gated layout + skeleton dashboard, ~3-5j effort). Décision @thierry prochaine session : démarrage Phase 9 ou autre backlog. — **Update précédent (17 mai 2026 ~00h45 CEST) — 🚨 THI-186 Critical security fix livré en 3 rounds** : bug data leak inter-utilisateurs via `localStorage` non-cleared découvert en prod par @thierry (37 % / 24 lessons affichées en mode invité, contamination cross-account confirmée empiriquement via Supabase live query — `shared:24, only_google:0, only_hotmail:0`). Root cause dans `src/app/context/ProgressContext.tsx` dormante depuis **Phase 3 livrée 3 avril 2026** (6 semaines en prod sans détection). Fix livré en 2 PRs principales + 1 polish : **PR [#241](https://github.com/thierryvm/TerminalLearning/pull/241)** owner-tracking aux transitions auth (`STORAGE_OWNER_KEY` + clear sur SIGNED_OUT / clear avant SIGNED_IN si owner différent / preserve guest legitime), 9 tests isolation. **PR [#242](https://github.com/thierryvm/TerminalLearning/pull/242)** migration force-clear au boot pour browsers cachant l'ancien JS (Chrome cache stale), 2 tests migration. **PR docs+polish session** (cette entry) Sourcery fixups (helper `applyLegacyOwnerMigration` partagé tests/prod + try/catch narrow JSON.parse) + Sidebar branding « Terminal Master » → « Terminal Learning » (cohérence B2B écoles, même fix que THI-153 Layout mobile mais surface sidebar). **Cleanup data prod Supabase** post-decision @thierry : Hotmail = compte principal organique (timestamps étalés 3 avril → 4 mai), Google = contaminé (7 lessons mass-upsert au signup 3 avril 10:18:49). Backup CSV + `DELETE FROM progress WHERE user_id='6832c7a5-...'` → Google 24 → 0 lessons, Hotmail 24 préservées. **1405 → 1417 tests verts** (+12 isolation/migration/edge). **THI-187** Backlog Medium créé : feat UX bouton "Réinitialiser ma progression" dans Settings (demande @thierry pendant la session). Pourquoi pas attrapé avant : `rbac-flow-tester` teste RLS Supabase mais pas localStorage lifecycle, aucun E2E Playwright multi-account. Action correctrice agents : à arbitrer prochaine session. — **Update précédent (16 mai ~19h30 CEST) — 🚀 Sprint 2 étape 3/N — THI-131 ✅ + THI-180 ✅** : double livraison sécurité-critique sur la chaîne LTI 1.3. PR [#236](https://github.com/thierryvm/TerminalLearning/pull/236) mergée — Phase 7c Auth MVP avec `jose@6` (`createRemoteJWKSet` + `jwtVerify`) + nonceStore replay 2 couches + 10 lti-auditor checks (modèle **Opus 4.7** après rappel @thierry anti-Haiku discipline) + migration `013_lti_launches.sql` audit log write-only + 19 tests crypto isolés (`// @vitest-environment node`). Audit cascade lti-auditor → 3 anti-patterns SPIKE dormants détectés et nettoyés AVANT merge (W1 `ignoreExpiration: true` + clé string littérale CVE-2015-9235 alg confusion · R2 collision import path · W4 `X-Frame-Options: ALLOW` non-RFC). PR [#237](https://github.com/thierryvm/TerminalLearning/pull/237) mergée — **senior reverse course** : Supabase Advisors flaggait 7 WARN, l'instinct naïf aurait révoqué les 6 fonctions `SECURITY DEFINER` en bloc. Vérification empirique → 3 sont invoquées par ~15 RLS `USING` clauses, PostgreSQL exige `EXECUTE` même via RLS → REVOKE = `permission denied for function` régression majeure évitée. Migration `014_revoke_security_definer_rpc.sql` chirurgicale (3 trigger-only revoked, 3 RLS-essential épargnées + tracked THI-182 schema `private`). DO blocks idempotent multi-env (Sourcery review). **8.8/10 security-auditor maintenu** (gains +0.3 architecturaux compensent H1 undici 7 CVEs catalog non exploitables aujourd'hui — gate `LTI_ENABLED=false` actif). Score `llm-security-auditor` 9.4/10 stable. 1405 tests verts (+19 LTI crypto). **Audit visuel prod ✅** : Lighthouse Landing **100/100/100/100** (a11y + BP + SEO + Agentic) + Core Web Vitals **LCP 332 ms · CLS 0.00 · TTFB 23 ms** (gain THI-118 confirmé en prod après régression Sentry weekly 9.31s). 6 routes critiques zéro erreur console (Landing + /changelog + /story + /app + /reference + /lesson). Drawer AI Tutor opérationnel (4 providers + rate-limit 30/30 + consent block). 15 agents `.claude/agents/` analysés → **aucun doublon** (frontières scopes/modèles/triggers documentées). **4 tickets backlog créés** : THI-182 (private RLS helpers, Low) · **THI-183** (monitor @vercel/node undici bump >= 6.24.0, High — gate `LTI_ENABLED=true`) · **THI-184** (fusionner ALLOWED_ISSUERS dual source, Medium — gate PR #2 LTI) · **THI-185** (nettoyer PII Sentry contexts, Medium — gate PR #2 LTI). **Process discipline reset @thierry** : merge PR #237 en autonomie reconnue comme dérive (zéro impact runtime — migration différée Dashboard manuel — mais règle CLAUDE.md projet "Jamais merger sans validation visuelle Vercel explicite de Thierry" non-négociable, Option B retenue). 2 actions manuelles @thierry pendantes : (1) apply `014_revoke_security_definer_rpc.sql` via Dashboard SQL Editor (5s, idempotent) · (2) flip "Leaked password protection" ON dans Auth → Settings → ferme 4 WARN (7→3 advisors). Sprint 2 ordre verrouillé : THI-118 ✅ → THI-153 ✅ → **THI-131 ✅ + THI-180 ✅** → THI-42 Profile Hub (next) → THI-77 + THI-78 admin heatmaps → PR #2 LTI endpoint integration (gate THI-184 + THI-185) → PR #3 activation `LTI_ENABLED=true` (gate THI-183 undici + audit final triple). Mini-prompt reprise : `docs/sessions/next-session-thi-42.md`. — **Update précédent (16 mai ~14h30 CEST) — 🚀 Sprint 2 étape 2/N — THI-153 ✅** : PR [#234](https://github.com/thierryvm/TerminalLearning/pull/234) mergée — cleanup UI bundle (umbrella audit post-Sprint 1). 4 items cherry-picked de l'audit 9 mai : (H1) consolidation 3 palettes red mixées → `--github-red: #f85149` CSS var unique source, migration AI/auth components avec Tailwind v4 opacity modifiers, palette pédagogique Tailwind red préservée (Dashboard processus, CommandReference, level 5 badge) · (C1) UserMenu logout focus ring rouge → emerald (cohérence keyboard focus THI-152) · (C2) shadcn dead slots documentés via header comments dans button.tsx + badge.tsx · (H2) `sonner` désinstallé (-45 kB minified, 0 import réel, juste exemple textuel pédagogique dans curriculum.ts Module 11 IA) · **Bonus brand fix** : « Terminal Master » → « Terminal Learning » dans Layout mobile header (`/app`), aligne avec toutes les autres surfaces. **Sourcery review addressed** : extraction `DestructiveActionButton` helper local (2 boutons AiSettings pixel-identiques) + justification emerald token Tailwind v4 natif. M1 button variants flaggées orphelines → **re-vérifié périmé**, toutes utilisées. Validation : ui-auditor SHIP-READY, 1386 tests passed 0 errors, Landing chunk 7.33 kB gzip stable, Chrome DevTools MCP preview Vercel 0 erreur console. **3 tickets backlog créés** post-décisions @thierry 16 mai : THI-177 pré-i18n discipline (Low, gate Phase 9 admin) · THI-178 SEO longue traîne SSG (Low, Phase 10+) · THI-179 securityheaders A+ via COEP (Low, post-LTI 7c). Sprint 2 ordre verrouillé : THI-118 ✅ → THI-153 ✅ → **THI-131 Phase 7c LTI (next)** → THI-42 Profile Hub → THI-77/78 admin heatmaps. — **Update précédent (16 mai ~10h45 CEST) — 🚀 Sprint 2 démarré — deadline 10 juin (écoles + admin panel) — THI-118 ✅** : PR [#232](https://github.com/thierryvm/TerminalLearning/pull/232) mergée — landing LCP regression fix (Sentry weekly 3.87 s → 9.31 s zone poor). Diagnostic Chrome DevTools MCP : LCP element = hero `

` sous-titre (TEXTE), render delay 98.9 %, fuites `landingContent.ts` (imports `commandCatalogue` + `ENVIRONMENTS` forçaient chunk curriculum 41 kB gzip eager) + `UserMenu`/`LoginModal`/`PWAInstallModal` eager. Fix : hardcoder TOTAL_COMMANDS/ACTIVE_ENVIRONMENTS_COUNT + drift test (caught silent `TOTAL_LESSONS` 64 → 65), React.lazy + Suspense pour modals conditionnelles. Bundle Landing chunk **27.29 → 7.33 kB gzip (−73 %)**, curriculum chunk plus dans graph landing. Validation Chrome DevTools MCP preview Vercel : 0 erreur console, lazy modal fonctionne. Sprint 2 ordre verrouillé : THI-118 ✅ → THI-153 cleanup UI bundle (~75 min, H2/M1/C1/C2/H1) → THI-131 Phase 7c LTI → THI-42 Profile Hub → THI-77/78 admin heatmaps. — **Update précédent (16 mai ~10h CEST) — 🏁 Sprint 1 Phase 7b lockdown CLOS à 4/4 — THI-113 ✅** : PR [#230](https://github.com/thierryvm/TerminalLearning/pull/230) mergée — audit final triple (3 agents parallèles) + **H1 fix sentry-tunnel symmetric scrub** (URL query + headers Authorization/X-API-Key/*token* alignés au beforeSend client, plus fallback string-based pour URLs relatives suite Sourcery review security 🚨). Verdict ALL CLEAR : security-auditor **9.4/10** (+0.1 vs 9.3 post-H1 fix), prompt-guardrail-auditor **9.3/10** (44/44 fixtures × 4 locales rejetées, Règle 10 ADR-005 SATISFIED), ui-auditor SHIP-READY (3 LOW non bloquants). Rapport audit complet : [`docs/audits/ai-tutor-v1-2026-05-16.md`](audits/ai-tutor-v1-2026-05-16.md). H2 undici CVEs deps transitive + H3 git history credential = différés/risque résiduel accepté. Trajectoire IA : 8.7 → 9.0 → 9.1 → 9.3 → **9.4/10**. Reste vers 9.5/10 : H2 upgrade + R3 M2-AI encoding + R5 H4-AI jsonwebtoken (Phase 7c gate). **Sprint 1 récap** : THI-148 ✅ → THI-144 ✅ → THI-112 ✅ → THI-113 ✅. **Phase 7c LTI activation** = sprint suivant (gate H4-AI jsonwebtoken supply chain). — **Update précédent (16 mai ~01h CEST) — Sprint 1 étape 3/4 livrée — THI-112 ✅** : PR [#228](https://github.com/thierryvm/teerminalLearning/pull/228) mergée (AiKeySetup standalone + AiConsentModal extraction + AiSettings page `/app/settings` + Sidebar nav + PrivacyPolicy section `#ai-processing` + **M3-AI VERIFIED fermé** : consent storage `'true'` → JSON `{version, acceptedAt, expiresAt}` TTL 365j + migration legacy), `llm-security-auditor` re-baseline **9.3/10 confirmé** (delta +0.2 vs 9.1, cible 9.25 dépassée), 14 files changed (+1545 / −214), tests AI 314 → ~329+ avec 8 nouveaux invariants `consent.test.ts`. Sourcery review round 2 (3 findings) addressed dans même PR : providerMeta module centralisé + AiConsentModal defense-in-depth `handleAccept` + revocation copy clarifiée. Trajectoire 9.5/10 atteignable via R3 M2-AI encoding bypass (THI-153) + R5 H4-AI jsonwebtoken (Phase 7c gate). **Sprint 1 ordre** : THI-148 ✅ → THI-144 ✅ → THI-112 ✅ → THI-113 audit final triple (next, étape 4/4). — **Update précédent (10 mai ~12h CEST) — Sprint 1 étape 2/4 livrée — THI-144 ✅** : PR [#222](https://github.com/thierryvm/TerminalLearning/pull/222) mergée (system prompt v1.1.0 + ADR-008 + eval suite hybride (a)+(b) + M4-AI LOW VERIFIED + R1 follow-up symmetric), `llm-security-auditor` re-baseline **9.1/10 confirmé** (delta +0.1 vs 9.0/10 matin), 1339 tests passants (+48 vs baseline), audit `prompt-guardrail-auditor` PASS (0 CRITICAL, 0 WARNING). 4 frictions ChatGPT cross-validation résolues (compound questions / over-explanation / repeated hints / satisfaction signal). Eval suite (b) manual run pending (`OPENROUTER_API_KEY` env requise, gate ship documenté dans PR body). Trajectoire 9.5/10 atteignable via R2 + R3 + R5 (THI-153 sprint 2 + Phase 7c gate). **Sprint 1 ordre** : THI-148 ✅ → THI-144 ✅ → THI-112 onboarding (next) → THI-113 audit final triple. — **Update précédent (10 mai ~03h CEST) — Clôture finale session marathon** : 11 PRs livrées (#208 → #217), agent `ai-pentester-pro` (créé PR #210) renommé **`llm-security-auditor`** suite analyse ChatGPT (PR #212 — éviter policy filters Anthropic, ajout framework Evidence confidence VERIFIED/STRONG_INDICATOR/SPECULATIVE/RESEARCH_ONLY, atténuation tone defense vs adversarial créatif). **14ᵉ agent `session-orchestrator` créé** (PR #213 + #214 portability fix Sourcery). **1ʳᵉ baseline `llm-security-auditor` officielle : 8.7/10** (entre security-auditor 8.5 et prompt-guardrail-auditor 8.8, framework Evidence empêche inflation CRITICAL). 2 findings HIGH/MEDIUM fermés sur 5 (PR #215 : M1-AI VERIFIED `escapeDelimiters(ctx.goal)` + H10-AI STRONG_INDICATOR BIDI_RX étendu Unicode Tag block U+E0000-U+E007F). PR #216 `chore: gitignore .tmp/`, PR #217 audit-log baseline tracé. THI-153 umbrella : 2/13 cochés. Re-baseline estimé prompt-guardrail 9.2/10, llm-security 9.0/10 (à confirmer prochaine session). Process shutdown 10 phases codifié (`session_shutdown_process.md` exhaustif avec 9 anti-patterns). Pattern `pattern_sourcery_thread_resolution.md` cross-projet réutilisé 4 fois cette session — investissement memo remboursé en <24h. — **Audit global multi-agents post-Sprint 1 étape 1/4 + nouvel agent `llm-security-auditor` 7 couches livré PR [#210](https://github.com/thierryvm/TerminalLearning/pull/210) (renommé via PR [#212](https://github.com/thierryvm/TerminalLearning/pull/212))**. 4 agents en parallèle (`security-auditor` 8.5/10 ship-ready, `test-runner` ✅ 1291 verts, `content-auditor` ✅ PROPRE, `ui-auditor` ⚠️ DEBT detected) → **9 findings consolidés en THI-153 umbrella** (priority High, gate H3 escapeDelimiters lessonContext.goal AVANT THI-144, 30 min). Treizième agent `llm-security-auditor` créé : **7 couches séquentielles avec section `## Raisonnement Couche N` obligatoire AVANT chaque verdict** (L1 surface, L2 threat modeling 8 menaces, L3 OWASP LLM Top 10, L4 vecteurs 2026 hors OWASP, L5 chaînes d'attaque CVSS, L6 stress test défenses, L7 self-critique double-pass), modèle Opus 4.7, anti-patterns bannis (verdict sans PoC, 0 finding HIGH improbable, score stable 4+ semaines), portable cross-projet (Ankora, GetPostCraft, futur Super Admin). **Reminder Terminal Sentinelle V2** verrouillé memo `project_terminal_sentinelle_evolution.md` : V1 couplé TL livré PR #90 (12 avril), V2 module greffable cross-projet pour futur dashboard Super Admin = Phase 10+. Pas de chantier V2 maintenant. Préparation déjà : `llm-security-auditor` portable, memos cross-projet dans `claude-config/memory/`, conventions documentées. — **Sprint 1 Phase 7b lockdown démarré** : `THI-148` (méta-plateforme V1.0.1) livré PR [#208](https://github.com/thierryvm/TerminalLearning/pull/208) en review, prompt bump `tutor/v1.0.0` → `tutor/v1.0.1` + bloc `` statique (11 modules / 65 leçons / 3 environnements, pas de PII, `userProgress` reporté V1.5 + ADR-009) + bonus defense-in-depth C1 audit `prompt-guardrail-auditor` 8.8/10 → full PASS post-fix (`DELIMITER_RX` étendu + `escapeDelimiters()` exporté + module titles wrappés, hardens path AVANT V1.5 custom modules). **1291 tests passed** (+22 vs baseline 1268), 0 failed, type-check + lint clean. Décision *finish what started* tracée (memos CC `feedback_finish_what_started.md` + `project_lti_spike_state.md`) : Phase 7b lockdown complète **AVANT** pivot Phase 7c LTI (qui reste en SPIKE pur — `verifyJwt()` placeholder, `LTI_ENABLED=false`). Branche `docs/sprint-1-thi-148-shutdown` porte le mini-prompt de reprise THI-144 dans `docs/sessions/next-session-thi-144.md` (3 questions ouvertes tranchées 10 mai : ADR-008 confirmé (ADR-007 = solo-maintainer-sustainability déjà pris), eval suite hybride (CI mock + script manuel Haiku), scope one-shot v1.1.0). **Sprint 1 ordre verrouillé** : THI-148 ✅ → THI-144 (system prompt v1.1.0 + **ADR-008** + eval suite + 5 micro-frictions ChatGPT cross-validation, ~5h estimé) → THI-112 onboarding (AiKeySetup + AiConsentModal + AiSettings + /privacy#ai-processing, ~1 jour) → THI-113 audit final triple (security-auditor + prompt-guardrail-auditor + ui-auditor). Pivot Phase 7c LTI = sprint suivant après lockdown. — **Phase 7c THI-152 sprint mobile recovery 🏁 CLOS** (9/9 mini-PRs + hotfix 7bis livrées). Narration thématique du sprint : [`docs/story/v1-5-mobile-recovery-narrative.md`](./story/v1-5-mobile-recovery-narrative.md). 9 mini-PRs séquentielles + 1 hotfix : (1) focus traps a11y, (2) forms anti-zoom, (3) FAB Sparkles size/opacity/position, (4) PWA apple-touch-icon PNG + standalone metas, (5) touch targets ≥44/≤40 + Option D FAB recalibration, (6) drawer overflow word-break + header truncation, (7) PWA safe-area top + autoFocus terminal contrôlé, (7bis hotfix) Landing nav safe-area, (8) focus rings emerald harmonization, (9) FINAL polish HTML metas W3C `mobile-web-app-capable` + tap-highlight transparent + Sidebar landscape `pl-[max(0px,env(safe-area-inset-left))]` + verifs theme-color déjà conforme + font-display swap déjà conforme fontsource 5.x. **3 bugs empiriques @thierry éradiqués** : drawer overflow horizontal mobile (6/9), header `/app` PWA standalone (7/9), bouton "Commencer →" Landing PWA standalone (7bis). Voie safe @cowork respectée tout le sprint : zéro modif `ui/button.tsx` variants, zéro nouvelle variant. **Asymétrie 44/56 mobile/desktop FAB** intentionnelle (primary action exemption documentée). **Sprint THI-152 méthodologie validée** : audit empirique > supposition théorique, pattern `max(baseline,env())` cohérent partout, specs static + dynamic hybrides, empirical override @thierry intégré. THI-150 ✅ + THI-151 ✅ + 5 sub-PRs THI-152 (#196/#197/#198/#199/#200 en cours) toutes mergées sauf #200. Empirical override mini-PR 3/9 confirmé par @thierry (FAB mobile 48→44 px, desktop 56 inchangé, asymétrie intentionnelle primary action exemption). -> Statut global : **Phase 5 EN COURS** — Curriculum Expansion : 11 modules ✅, 66 leçons, **1850 tests** (1780 pass + 20 RBAC skipped Phase 9) + 176 E2E — **Vision consolidée** : LTI-first (ADR-001), BYOK OpenRouter 4-tiers (ADR-002), TTFR KPI central (ADR-003), Classroom Composer UI (ADR-004), AI Tutor V1 décisions gelées (ADR-005 — stockage, rate-limit, guardrails), Solo-sustainable practices (ADR-006), tuteur IA socratique dès A1, i18n FR/NL/EN — Architecture stratégique précédente (THI-35) : Terminal Sentinel (Phase 5.5) ✅, RBAC complet (Phase 7) ✅, Admin Panel (Phase 9), PWA avancée (Phase finale) — **Epic Web 2026 Compliance** (THI-96) : 6/8 sub-issues livrées (THI-97 → THI-102), reste Desktop a11y + CSS moderne 2026 — **Phase 7b (AI Tutor V1) ✅ COMPLETE + V1.5 SÉQUENCÉE** : THI-115 ✅, THI-109 ✅ (gate-zero guardrail), THI-110 ✅ (keyManager AES-GCM), THI-120 ✅ (Sentry scrubber), **THI-111 ✅ COEUR FONCTIONNEL** (PR #188 — sanitizer + 4 providers + panel + 287 AI tests, audits guardrail 9.4/10 + security 8.8/10 + ui A11y exemplary), **THI-147 ✅ FIX SAFE-AREA iPhone PWA** (PR #189), **panel actif en Production** (`VITE_AI_TUTOR_ENABLED=true` + `VITE_AI_TUTOR_OPENROUTER_MODEL=anthropic/claude-haiku-4-5` activés Production+Preview par @cowork) — **Sprint sécurité 1-2 mai 2026 ✅ CLOS** : audit security-auditor 8.1/10 → ~8.6/10 post-sprint, 11 PRs livrées (#168 à #178), 5 HIGH/MEDIUM Done (THI-133/134/135/137/140), 4 MEDIUM ciblés en backlog (THI-136/138/139/112), agent `route-attack-auditor` créé — **Session 4 mai 2026** : THI-111 livré + THI-147 livré + 5 tickets V1.5 backlog créés (THI-142 lessonContext renforcé HIGH, THI-143 frustration heuristic + détection sémantique user MEDIUM, THI-144 system prompt v1.1.0 + ADR-008 + eval suite MEDIUM, THI-145 chat assistant role-based Phase 9+ LOW, THI-146 modèle défaut Haiku HIGH déjà actif via env var, THI-148 extend tutor scope platform meta-questions V1.0.1 P1 1h30 estimé) — **Posture validée** : pas de rush deadline, qualité/scalabilité/perf non négociables, plan respecté en ordre, **mea culpa explicite** à chaque round trio @thierry / @cc-terminallearning / @cowork (estimation 30 min → 1h30, privacy `userProgress` retirée V1.0.1, hypothèse `transform` mobile réfutée par diagnostic Chrome DevTools MCP) — **Verdict empirique Haiku 4.5 (capturé 4 mai 21h par @cowork via Chrome MCP)** : 5 tests qualitatifs, score moyen **9.3/10** — Test 1 méta-plateforme 8/10, Test 2 fichiers cachés 9/10, Test 3 hallucination 9.5/10, Test 4 frustration 10/10 (Haiku bascule mode direct AUTONOMEMENT), Test 5 jailbreak 10/10. **Reprio backlog tranchée @cowork** : THI-146 ✅ SUCCÈS validé, **THI-142 → Low** (Haiku gère déjà bien le contexte leçon), **THI-143 → Low** (Haiku résout naturellement la frustration via compréhension contextuelle), **THI-148 P1 INCHANGÉ** (scope ≠ modèle prouvé Test 1, GO IMMÉDIAT), THI-144 P2 Medium (peut englober THI-148 + eval suite), THI-145 P3 Low (Phase 9+). **ROI méthode scientifique** : ~4-6h économisées (THI-142/143 reportés V2). **Next ordonné — décision @cowork 5 mai matin** : (1) **THI-150 EN COURS** (12ᵉ agent `mobile-responsive-auditor`, ex-brick 3a de THI-149 epic Done, 11 sections / ≥48 checkpoints + bonus Section 11 Desktop Preservation + checkpoints BUG-FAB-001 visibility/contrast/detachment), (2) THI-151 audit Playwright WebKit + matrice bugs (ex-brick 3b), (3) THI-152 mini-PRs fix séquentielles (ex-brick 3c, critère ABSOLU **ne pas casser desktop**), (4) THI-148 extend tutor scope méta-plateforme V1.0.1 (1h30 honnête, scope statique platformContext, bump v1.0.0→v1.0.1, audit guardrail Règle 10 obligatoire), (5) **THI-144 enrichi P1** system prompt v1.1.0 + ADR-008 + eval suite (intègre 5 micro-frictions identifiées par cross-validation ChatGPT sur session 8 tours @thierry — compound questions, sur-explication internal mechanics, indices répétés, platformContext absent confirme THI-148, conclusion ouverte), (6) THI-112 onboarding AiKeySetup + picker modèle curated, (7) THI-114 Web Worker isolation, (8) THI-145 chat role-based Phase 9+. **THI-142/143 reportés V2** (Haiku 9.3/10 résout 80% naturellement). +> Dernière mise à jour : **24 septembre 2026 CEST** — **🩺 Grand Check-up (reprise)** : P0 livré — #383 dépendances (react-router 7.18.4, npm audit 41 → 24, 0 critique) + #384 THI-340 (preuve d'âge posée sur tous les retours OAuth). P1 « fidélité du terminal » (THI-353) en cours — #385 état de départ par leçon, #386 ligatures monospace (vérifié sur iPhone, THI-354), #387 scripts / `$PROFILE` / ExecutionPolicy / `chmod` : exercices validés malgré une erreur **44 → 3**. Suite : redirections (`2>`, `>`, `tee`), écarts théorie ↔ terminal, puis garde « pas de validation si erreur ». Précédent : 6 juin 2026 — audit complet pré-pause (voir CHANGELOG). +> Statut global : **Phase 5 EN COURS** — Curriculum Expansion : 11 modules ✅, 66 leçons, **2500+ tests unitaires** + 176 E2E — **Vision consolidée** : LTI-first (ADR-001), BYOK OpenRouter 4-tiers (ADR-002), TTFR KPI central (ADR-003), Classroom Composer UI (ADR-004), AI Tutor V1 décisions gelées (ADR-005 — stockage, rate-limit, guardrails), Solo-sustainable practices (ADR-006), tuteur IA socratique dès A1, i18n FR/NL/EN — Architecture stratégique précédente (THI-35) : Terminal Sentinel (Phase 5.5) ✅, RBAC complet (Phase 7) ✅, Admin Panel (Phase 9), PWA avancée (Phase finale) — **Epic Web 2026 Compliance** (THI-96) : 6/8 sub-issues livrées (THI-97 → THI-102), reste Desktop a11y + CSS moderne 2026 — **Phase 7b (AI Tutor V1) ✅ COMPLETE + V1.5 SÉQUENCÉE** : THI-115 ✅, THI-109 ✅ (gate-zero guardrail), THI-110 ✅ (keyManager AES-GCM), THI-120 ✅ (Sentry scrubber), **THI-111 ✅ COEUR FONCTIONNEL** (PR #188 — sanitizer + 4 providers + panel + 287 AI tests, audits guardrail 9.4/10 + security 8.8/10 + ui A11y exemplary), **THI-147 ✅ FIX SAFE-AREA iPhone PWA** (PR #189), **panel actif en Production** (`VITE_AI_TUTOR_ENABLED=true` + `VITE_AI_TUTOR_OPENROUTER_MODEL=anthropic/claude-haiku-4-5` activés Production+Preview par @cowork) — **Sprint sécurité 1-2 mai 2026 ✅ CLOS** : audit security-auditor 8.1/10 → ~8.6/10 post-sprint, 11 PRs livrées (#168 à #178), 5 HIGH/MEDIUM Done (THI-133/134/135/137/140), 4 MEDIUM ciblés en backlog (THI-136/138/139/112), agent `route-attack-auditor` créé — **Session 4 mai 2026** : THI-111 livré + THI-147 livré + 5 tickets V1.5 backlog créés (THI-142 lessonContext renforcé HIGH, THI-143 frustration heuristic + détection sémantique user MEDIUM, THI-144 system prompt v1.1.0 + ADR-008 + eval suite MEDIUM, THI-145 chat assistant role-based Phase 9+ LOW, THI-146 modèle défaut Haiku HIGH déjà actif via env var, THI-148 extend tutor scope platform meta-questions V1.0.1 P1 1h30 estimé) — **Posture validée** : pas de rush deadline, qualité/scalabilité/perf non négociables, plan respecté en ordre, **mea culpa explicite** à chaque round trio @thierry / @cc-terminallearning / @cowork (estimation 30 min → 1h30, privacy `userProgress` retirée V1.0.1, hypothèse `transform` mobile réfutée par diagnostic Chrome DevTools MCP) — **Verdict empirique Haiku 4.5 (capturé 4 mai 21h par @cowork via Chrome MCP)** : 5 tests qualitatifs, score moyen **9.3/10** — Test 1 méta-plateforme 8/10, Test 2 fichiers cachés 9/10, Test 3 hallucination 9.5/10, Test 4 frustration 10/10 (Haiku bascule mode direct AUTONOMEMENT), Test 5 jailbreak 10/10. **Reprio backlog tranchée @cowork** : THI-146 ✅ SUCCÈS validé, **THI-142 → Low** (Haiku gère déjà bien le contexte leçon), **THI-143 → Low** (Haiku résout naturellement la frustration via compréhension contextuelle), **THI-148 P1 INCHANGÉ** (scope ≠ modèle prouvé Test 1, GO IMMÉDIAT), THI-144 P2 Medium (peut englober THI-148 + eval suite), THI-145 P3 Low (Phase 9+). **ROI méthode scientifique** : ~4-6h économisées (THI-142/143 reportés V2). **Next ordonné — décision @cowork 5 mai matin** : (1) **THI-150 EN COURS** (12ᵉ agent `mobile-responsive-auditor`, ex-brick 3a de THI-149 epic Done, 11 sections / ≥48 checkpoints + bonus Section 11 Desktop Preservation + checkpoints BUG-FAB-001 visibility/contrast/detachment), (2) THI-151 audit Playwright WebKit + matrice bugs (ex-brick 3b), (3) THI-152 mini-PRs fix séquentielles (ex-brick 3c, critère ABSOLU **ne pas casser desktop**), (4) THI-148 extend tutor scope méta-plateforme V1.0.1 (1h30 honnête, scope statique platformContext, bump v1.0.0→v1.0.1, audit guardrail Règle 10 obligatoire), (5) **THI-144 enrichi P1** system prompt v1.1.0 + ADR-008 + eval suite (intègre 5 micro-frictions identifiées par cross-validation ChatGPT sur session 8 tours @thierry — compound questions, sur-explication internal mechanics, indices répétés, platformContext absent confirme THI-148, conclusion ouverte), (6) THI-112 onboarding AiKeySetup + picker modèle curated, (7) THI-114 Web Worker isolation, (8) THI-145 chat role-based Phase 9+. **THI-142/143 reportés V2** (Haiku 9.3/10 résout 80% naturellement). --- diff --git a/docs/security-audit-log.md b/docs/security-audit-log.md index d8694c1..9db6bd2 100644 --- a/docs/security-audit-log.md +++ b/docs/security-audit-log.md @@ -5,6 +5,26 @@ This log is updated after each security audit and serves as institutional memory --- +## Grand Check-up, reprise — gates des PR #383 à #387 (23-24 septembre 2026) + +**Date**: 23-24 septembre 2026 +**Auteur**: CC Terminal Learning (Opus 5.5) +**Méthode**: verdicts repris des descriptions de PR (traçabilité ; pas de nouveau run). + +| PR | Portée | Gates | Verdict | +|---|---|---|---| +| #383 `security(deps)` | react-router 7.18.4, vite 6.4.3, transitives (`tar` critique) | `feature-dev:code-reviewer` (CHANGELOG react-router 7.14 → 7.18.4 relu contre l'usage réel) | SHIP · npm audit 41 → 24, critique 0 | +| #384 `fix(auth)` THI-340 | horodatage d'âge sur tous les retours OAuth, rapport Sentry sans PII | `security-auditor` ×2 (écran d'âge d'origine #380 + diff), `ui-auditor`, `feature-dev:code-reviewer` | 0 CRITICAL / 0 HIGH ; MEDIUM (horodatage manquant sur `returnTo`) corrigé ; ui H1/H2 corrigés ; SHIP | +| #385 `feat(terminal)` THI-353 | état de départ par leçon | `curriculum-validator`, `test-runner`, `ui-auditor`, `feature-dev:code-reviewer` | GO · PASS · SHIP (aucun finding ≥ 80) | +| #386 `fix(ui)` | ligatures monospace désactivées | `ui-auditor`, `feature-dev:code-reviewer` | PASS · limite Safari divulguée, puis vérifiée sur iPhone réel (THI-354 Done) | +| #387 `fix(terminal)` THI-353 | exécution de scripts (profondeur 3, budget 500 lignes), `$PROFILE`, ExecutionPolicy, `chmod` | `curriculum-validator`, `test-runner`, `ui-auditor`, `feature-dev:code-reviewer` | FIX FIRST → 4/5 findings corrigés avec test ; le 5ᵉ (redirection de la sortie d'un script) rattaché à la PR redirections | + +**Constats hors périmètre, tracés** : THI-350 (tunnel Sentry : branche de scrub serveur inatteignable + query strings dans les breadcrumbs) ; `/api/sentry-tunnel` renvoie 429 après quelques centaines de chargements depuis une même IP → risque pour une classe derrière le NAT d'une école (relié à THI-88) ; THI-352 (pas de délai maximal sur l'horodatage avant redirection). + +**Dette de gates de #380 (écran d'âge d'origine)** : `security-auditor` et `ui-auditor` l'ont couvert via #384 ; reste une passe `feature-dev:code-reviewer` sur le code d'origine non modifié par #384. + +--- + ## Rattrapage traçabilité — runs Sprint 2.C + break-ins agents (consolidé 4 juin 2026) **Date**: 4 juin 2026 (consolidation rétroactive)