diff --git a/05_activity_log/2026-07-31.md b/05_activity_log/2026-07-31.md
index 7047331..aa1c534 100644
--- a/05_activity_log/2026-07-31.md
+++ b/05_activity_log/2026-07-31.md
@@ -1,5 +1,77 @@
# Activity Log · 2026-07-31 · Claude Code DTP
+## Session `20260731_015954` · Buffer S8 · « Vert trompeur » : les 6 gates `ci/*.sh` passaient VERT hors d'un arbre git en ne contrôlant RIEN
+
+**Tâche** : **Sprint 8 · buffer L75 (« Regression tests exhaustifs » · DevOps
+CI/CD)**. Roadmap fonctionnellement close (21/21 modules 100/100, 534 tests verts,
+6 gates statiques). Poursuite de la série anti-dérive : recherche d'un **faux-vert
+réel** (gate qui passe sans rien vérifier), même classe que les bugs
+`regression_run.json` / `out/` RBAC des sessions précédentes.
+
+### Défaut trouvé — `cd "$(git rev-parse --show-toplevel)"` échoue en SILENCE hors d'un checkout git
+
+- **Mécanisme** : les **6** gates commençaient par
+ `cd "$(git rev-parse --show-toplevel)"`. HORS d'un arbre de travail git (tarball,
+ `git archive | tar -x`, `git` absent du PATH, working-dir erroné), `git rev-parse`
+ n'écrit **rien** sur stdout → la commande devient `cd ""`, un **no-op qui RETOURNE
+ SUCCÈS** (le script reste dans le cwd au lieu d'échouer). Les gates poursuivaient
+ sur un `git ls-files` renvoyant une liste **VIDE** →
+ `validate_json` (« Aucun fichier .json suivi — rien à valider »),
+ `guard_constraints` et `check_docs` sortaient **exit 0 VERT en ayant contrôlé
+ ZÉRO fichier** (≈50 JSON non validés, contraintes CLAUDE.md non vérifiées). Un
+ gate qui passe au vert sans rien enforcer est le **pire** « vert trompeur » —
+ précisément ce que la série buffer traque.
+- **Preuve au sol (avant fix)** : copie de l'arbre **sans `.git`** →
+ `validate_json` **exit 0** « rien à valider » ; `guard_constraints` **exit 0**
+ « Contraintes respectées » ; `check_docs` **exit 0**. Ce piège avait déjà faussé
+ une vérif « 6 gates verts sur git archive » d'une session antérieure (un `git
+ archive | tar -x` n'a **pas** de `.git`, alors que le runner Gitea a un vrai
+ `.git` via `actions/checkout@v4` — donc la méthode « archive » n'est PAS
+ équivalente au runner pour tout check qui interroge git).
+
+### Fix (2 solutions · CLAUDE.md #4 — retenu : helper partagé DRY + durcissement INV-B)
+
+- **Correctif de fond** : nouveau helper **`ci/lib.sh`** (sourcé, jamais exécuté
+ seul) exposant **`cd_repo_root`** — se positionne à la racine, ou **échoue
+ bruyamment** (`exit 3`, code distinct d'un échec de contrôle `1`) avec un message
+ clair si `git rev-parse` ne renvoie rien. Les 6 gates remplacent leur
+ `cd "$(git rev-parse …)"` par `source "$(dirname "${BASH_SOURCE[0]}")/lib.sh" ||
+ exit 3 ; cd_repo_root`. *(Alt. écartée : inliner le garde de 4 lignes × 6 fichiers
+ = doublon, contraire à CLAUDE.md #5 · préférence lib partagée.)*
+- **Durcissement du gate de câblage (INV-B · `check_ci_integrity.sh`)** : INV-B
+ exigeait que **chaque** `ci/*.sh` soit lancé par un job — `ci/lib.sh` (jamais dans
+ un `run:`) aurait été flaggé « gate mort ». INV-B accepte désormais un `ci/*.sh`
+ câblé de **deux** façons : lancé par un job du gate, **OU sourcé** par ≥1 gate
+ script (détecté par grep d'une ligne `source …/` / `. …/`, `#` exclu).
+ C'est un **renforcement** réel : le gate prouve maintenant aussi que les libs
+ partagées sont **effectivement sourcées** (pas du code mort).
+
+### Preuves de morsure
+
+- **Faux-vert corrigé** : arbre copié **sans `.git`** → les 6 gates sortent
+ désormais **exit 3** avec « ❌ ERREUR gate : hors d'un arbre de travail git »
+ (avant : `validate_json`/`guard_constraints`/`check_docs` = **exit 0 vert**).
+ Idem sur `git archive HEAD | tar -x` **après commit**.
+- **INV-B reconnaît la lib** : `ci/lib.sh — lib sourcée par [les 6 gates] · non
+ exécutée en propre (OK)` → **exit 0**.
+- **INV-B attrape une lib morte** : `ci/_dead.sh` (sourcé par personne) ⇒
+ `✗ … lancé/sourcé par AUCUN job CI (gate mort)` ⇒ **exit 1**.
+
+**Non-régression** : **6 gates verts** dans l'arbre de travail (= ce que voit le
+runner via `actions/checkout@v4`, avec un vrai `.git`) ; matrice **534/21 · PASS
+inchangée** ; `qa/audit_4big` **out/ inchangé** (les édits `ci/` sont **hors**
+`05_deliverables_mvp` → aucune dérive de consommateur) ; `ci.yml` YAML **valide**.
+
+**Anti-invention (#6)** : rien inventé — garde purement défensif (bash/git stdlib),
+INV-B relit `ci.yml` + grepe les gates sur disque à chaque run.
+
+**Hors périmètre worker (VPS · #8)** : néant (stdlib en-repo ; enregistrement du
+runner Gitea toujours du ressort DevOps).
+
+**Auto-score 4Big** : 96/100.
+
+---
+
## Session `20260731_012944` · Buffer S8 · Reproductibilité en CHECKOUT PROPRE — le hand-off out/ des 4 générateurs RBAC était `.gitignore`é → gate rouge en CI, vert en local seulement
**Tâche** : **Sprint 8 · buffer L75 (« Regression tests exhaustifs » · DevOps