diff --git a/05_activity_log/2026-07-31.md b/05_activity_log/2026-07-31.md index 3704161..7f61987 100644 --- a/05_activity_log/2026-07-31.md +++ b/05_activity_log/2026-07-31.md @@ -1,5 +1,58 @@ # Activity Log · 2026-07-31 · Claude Code DTP +## Session `20260731_120054` · Buffer S8 · Domaine Démo : le run-sheet de pitch (`demo/scenarios`) affichait « **10 modules cités** » dans le diagramme d'architecture de son README — un compte data-derived transcrit À LA MAIN sans AUCUN gate + +**Tâche** : **Sprint 8 · buffer L75** (DevOps CI/CD · QA régression). Roadmap +fonctionnellement close ; poursuite de la série anti-dérive (CLAUDE.md #6). Les +sessions précédentes ont gaté les domaines tests, portails, RBAC (3 volets), SEO, CRM +(pipeline vente), mobile, Legal (CONFOTUR), QA (audit 5D) et DevOps (run-book). Cette +session cible le **domaine Démo** — le **run-sheet de pitch**, dont un compte +data-derived cité dans le README pointait l'artefact généré sans être recomputé par +aucun gate. + +### Défaut trouvé — compte ungated, transcrit dans le diagramme du README + +- **`05_deliverables_mvp/demo/scenarios/README.md:23`** : diagramme d'architecture → + « `scenario_spec.json … out/ des modules livrés (`**`10 modules cités`**`)` ». +- L'artefact source est byte-gaté par `check_artifacts` : + `demo/scenarios/out/MANIFEST.json` recompte `counts.modules_cites_uniques` + (= |union des `modules_cites` des deux scénarios, résolus par pointeur RFC 6901|) à + chaque build. Le module ne saisit **aucune** valeur métier ; il résout depuis le + disque les hand-off amont que les beats citent. +- Aucun gate ne comparait la VALEUR écrite à l'artefact : ajouter un beat qui cite un + module encore non tissé (**11**) ou retirer la dernière citation d'un module d'un + scénario (**9**) ferait dériver le diagramme en silence — « vert trompeur » qu'aucune + suite `tests/` (qui teste des FONCTIONS, pas la prose du README) n'attrape. +- **État courant** : **aucun nombre périmé** — 10 recoupe MANIFEST exactement + (anti-invention #6 : rien à réécrire). Le défaut est la **surface ungated**. + +**Nouveau bloc dans `ci/check_readme_claims.sh`** : recompute le nombre depuis +`counts.modules_cites_uniques` (jamais figé ici · zéro duplication du compteur du +générateur #5) et exige l'égalité avec le README. Cohérence croisée en bonus : +`counts.modules_cites_uniques == |modules_cites|` (liste dédupliquée du manifeste, +elle-même sans doublon) — mord un compteur désynchronisé de sa propre liste. Un claim +absent échoue AUSSI (traçabilité). + +### Preuve (2 morsures vérifiées + restauration verte) + +| Injection | Résultat | +|---|---| +| README drift `10→11` | ✗ dit 11 modules cités MAIS artefact 10 · exit 1 | +| README claim supprimé (reformulé) | ✗ claim « (N modules cités) » INTROUVABLE · exit 1 | +| restauré | ✓ 10 modules cités == artefact · exit 0 | + +- **`ci/README.md`** — surface ajoutée à la table récap du pipeline + paragraphe + descriptif (classe « run-sheet de démo »). + +Suite CI complète re-verte : `guard_constraints` · `validate_json` · `check_docs` · +`check_artifacts` · `check_regression` · `check_ci_integrity` · `check_readme_claims`. + +**Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo). + +**Auto-score 4Big** : 96/100. + +--- + ## Session `20260731_113048` · Buffer S8 · Domaine DevOps : le run-book de déploiement VPS (`devops/deploy_runbook`) affichait « **7 phases** » (fiche agent devops, ×2) et « **7 confirmations** préalables » (README du module) — deux nombres data-derived transcrits À LA MAIN dans DEUX docs sans AUCUN gate **Tâche** : **Sprint 8 · buffer L75** (DevOps CI/CD · QA régression). Roadmap diff --git a/ci/README.md b/ci/README.md index 8259cd3..20d42ea 100644 --- a/ci/README.md +++ b/ci/README.md @@ -20,7 +20,7 @@ et manuellement (`workflow_dispatch`). Jobs statiques (+ une suite `unittest` pa | `check-artifacts` | `ci/check_artifacts.sh` | Reproductibilité : chaque `out/*.json` versionné == build frais | ✅ oui | | `check-regression` | `ci/check_regression.sh` | Fraîcheur : `qa/regression/out/regression_run.json` (`run`) == run frais + verdict PASS | ✅ oui | | `check-ci-integrity` | `ci/check_ci_integrity.sh` | Intégrité du câblage : `gate.needs` == tous les jobs non-manuels · chaque `ci/*.sh` câblé au gate | ✅ oui | -| `check-readme-claims` | `ci/check_readme_claims.sh` | Intégrité des chiffres des docs d'entrée : chaque nombre de `README.md` « État courant », de la fiche QA « Verdict agrégé » **et** de la colonne « Tests » par suite des tables de fiches **et** des agrégats en prose (Total CRM / RBAC / e-CF) **et** des comptes par workspace des portails (cartes/liens/rôles, ×2 docs) **et** des comptes RBAC des 3 volets (table Nb rôles/profil, ligne agrégée apply_plan, ventilation userperm) **et** des comptes de mots-clés SEO (total + fr/en/es, ×2 docs) **et** du pipeline vente CRM (états/transitions, ×2 docs) **et** de l'app mobile Expo (onglets/rôles/langues/identifiants a_confirmer) **et** du DocType CONFOTUR (champs/données/sections/rôles/évènements de dépôt, ×2 docs) **et** de l'audit 5D (contrôles/dimensions ×2 docs + ventilation verdict pass/fail/à-confirmer) **et** du run-book de déploiement VPS (phases ×2 dans la fiche devops + confirmations préalables count & énumération dans le README) == artefact cité (#6) | ✅ oui | +| `check-readme-claims` | `ci/check_readme_claims.sh` | Intégrité des chiffres des docs d'entrée : chaque nombre de `README.md` « État courant », de la fiche QA « Verdict agrégé » **et** de la colonne « Tests » par suite des tables de fiches **et** des agrégats en prose (Total CRM / RBAC / e-CF) **et** des comptes par workspace des portails (cartes/liens/rôles, ×2 docs) **et** des comptes RBAC des 3 volets (table Nb rôles/profil, ligne agrégée apply_plan, ventilation userperm) **et** des comptes de mots-clés SEO (total + fr/en/es, ×2 docs) **et** du pipeline vente CRM (états/transitions, ×2 docs) **et** de l'app mobile Expo (onglets/rôles/langues/identifiants a_confirmer) **et** du DocType CONFOTUR (champs/données/sections/rôles/évènements de dépôt, ×2 docs) **et** de l'audit 5D (contrôles/dimensions ×2 docs + ventilation verdict pass/fail/à-confirmer) **et** du run-book de déploiement VPS (phases ×2 dans la fiche devops + confirmations préalables count & énumération dans le README) **et** du run-sheet de démo (nombre de modules cités dans le diagramme du README) == artefact cité (#6) | ✅ oui | | `gate` | — | Agrégat vert = gate qualité 4Big franchi | ✅ oui | Aucune dépendance réseau/marketplace hors `actions/checkout`. Tout tourne avec @@ -247,6 +247,17 @@ confirmations du README (les tokens `snake_case`) doit être l'ensemble **exact* `confirmations_ouvertes` — mord l'**échange** d'un nom de confirmation là où le seul compte (reste 7) resterait aveugle. +Même classe enfin sur le **run-sheet de démo** (livrable `demo/scenarios`) : le +nombre de **modules cités**, transcrit **à la main** dans le diagramme d'architecture +du README du module (« out/ des modules livrés (**10 modules cités**) »), pointait +l'artefact généré sans qu'aucun gate ne le recompute. Il est recomputé depuis +`demo/scenarios/out/MANIFEST.json` (`counts.modules_cites_uniques` = |union des +`modules_cites` des deux scénarios|, byte-gaté par `check_artifacts`) et comparé au +README ; ajouter un beat qui cite un module non encore tissé (11), ou retirer la +dernière citation d'un module (9), ferait dériver le diagramme en silence. Cohérence +croisée en bonus : `counts.modules_cites_uniques` == |`modules_cites`| (liste +dédupliquée du manifeste) — mord un compteur désynchronisé de sa propre liste. + Un claim **absent** échoue aussi (la dérive de formulation qui ferait disparaître un chiffre est elle-même une régression de traçabilité). stdlib pur (bash/git/python3), zéro réseau. diff --git a/ci/check_readme_claims.sh b/ci/check_readme_claims.sh index ac261fd..a43a1db 100755 --- a/ci/check_readme_claims.sh +++ b/ci/check_readme_claims.sh @@ -977,6 +977,57 @@ if dr_ph is not None: bad(f"DevOps · {DR_RD} — énumération {listed} ≠ confirmations_ouvertes " f"{sorted(dr_open)} → régénérer le README") +# ============================================================================ +# Démo · run-sheet de pitch (nombre de modules cités) — même classe de « vert +# trompeur », transcrit À LA MAIN dans le diagramme d'architecture du README du +# module, sans AUCUN gate. +# ---------------------------------------------------------------------------- +# Le générateur demo_scenario_gen émet out/MANIFEST.json (byte-gaté par +# check_artifacts ⇒ faisant autorité) dont `counts.modules_cites_uniques` est +# RECOMPTÉ à chaque build = |union des `modules_cites` des deux scénarios| — les +# hand-off amont que les beats citent par pointeur RFC 6901 (le module ne saisit +# AUCUNE valeur métier ; il résout depuis le disque). Ce nombre est recopié À LA +# MAIN dans le diagramme du README du module (05_deliverables_mvp/demo/scenarios/ +# README.md — « out/ des modules livrés (10 modules cités) »). Aucun gate ne le +# couvrait : ajouter un beat qui cite un module encore non tissé (11) ou retirer +# la dernière citation d'un module d'un scénario (9) ferait dériver le diagramme +# en silence pendant que le MANIFEST byte-gaté dit autre chose — dérive qu'aucune +# suite tests/ (qui teste des FONCTIONS, pas la prose du README) n'attrape. Même +# piège que les mots-clés SEO, les triplets par workspace des portails, le +# quintuplet CONFOTUR ou la synthèse audit_5d. On RECOMPUTE depuis +# counts.modules_cites_uniques (jamais figé ici · zéro duplication du compteur du +# générateur #5) et on exige l'égalité. Cohérence croisée en bonus : +# counts.modules_cites_uniques == |modules_cites| (liste dédupliquée du manifeste, +# elle-même sans doublon) — mord un compteur désynchronisé de sa propre liste. Un +# claim absent échoue AUSSI (la disparition d'un chiffre est une régression). +# ============================================================================ +DEMO_RD = f"{D}/demo/scenarios/README.md" +try: + demo_man = load("demo/scenarios/out/MANIFEST.json") + demo_n = str(demo_man["counts"]["modules_cites_uniques"]) + demo_list = demo_man["modules_cites"] + if len(demo_list) != len(set(demo_list)): + bad(f"Démo · incohérence artefact : modules_cites contient des doublons " + f"({len(demo_list)} entrées, {len(set(demo_list))} uniques)") + if demo_n != str(len(demo_list)): + bad(f"Démo · incohérence artefact : counts.modules_cites_uniques={demo_n} " + f"MAIS |modules_cites|={len(demo_list)}") +except (OSError, KeyError, ValueError) as e: + bad(f"Démo · artefact scenarios illisible/inattendu : {e}"); demo_n = None +if demo_n is not None: + demo_rd = read_norm(DEMO_RD) + if demo_rd is not None: + m = re.search(r"out/ des modules livrés \((\d+) modules cités\)", demo_rd) + if m is None: + bad(f"Démo · {DEMO_RD} — claim « (N modules cités) » INTROUVABLE " + f"(attendu modules_cites_uniques={demo_n})") + elif m.group(1) == demo_n: + good(f"Démo · {DEMO_RD} — {m.group(1)} modules cités == artefact " + f"(modules_cites_uniques={demo_n})") + else: + bad(f"Démo · {DEMO_RD} — dit {m.group(1)} modules cités MAIS artefact dit " + f"{demo_n} → régénérer le README") + sys.exit(1 if FAIL else 0) PY rc=$?