diff --git a/05_activity_log/2026-07-31.md b/05_activity_log/2026-07-31.md index 7a0a07c..39a148b 100644 --- a/05_activity_log/2026-07-31.md +++ b/05_activity_log/2026-07-31.md @@ -1,5 +1,55 @@ # Activity Log · 2026-07-31 · Claude Code DTP +## Session `20260731_133057` · Buffer S8 · Domaine QA/Audit 4Big : le **barème de notation** de l'auditeur (« **5 critères 4Big déterministes** » + la table `Critère | Poids` + l'archétype « `generator` (5 critères) ») était transcrit À LA MAIN dans le README du module sans AUCUN gate — le bloc racine ne gatait que sa ligne « Résultat courant » (verdict/modules), jamais le référentiel de scoring lui-même. + +**Tâche** : **Sprint 8 · buffer L75** (DevOps CI/CD · QA). Roadmap fonctionnellement +close ; poursuite de la série anti-dérive (CLAUDE.md #6). Restait une surface de « vert +trompeur » non gatée dans le module qui NOTE les autres : son propre barème. + +**Dérive silencieuse fermée** : +- `05_deliverables_mvp/qa/audit_4big/README.md` retranscrit trois surfaces + data-derived du référentiel de scoring : la prose « Note chaque module sur **5 + critères 4Big déterministes** », la table « Critère | Poids | Fait vérifié » (une + ligne `id`→poids par critère : DOC 20 · CONTRAT 20 · TESTS 25 · CLI 15 · HANDOFF 20) + et l'archétype « `generator` (5 critères) ». +- La source faisant autorité est `out/quality_report.json.criteria` (byte-gaté par + `check_artifacts`, recomputé du référentiel à chaque build). +- Aucun gate ne comparait la VALEUR écrite à l'artefact : ajouter/retirer un critère + ou changer un poids ferait dériver le README en silence — dérive qu'aucune suite + `tests/` (qui teste des FONCTIONS, pas la prose) n'attrape. + +**Nouveau bloc dans `ci/check_readme_claims.sh`** : recompute le **nombre de critères** +(prose + « generator »), et **chaque poids** depuis `criteria[].weight` (zéro +duplication du barème du générateur · #6), exige l'égalité avec le README. Cohérences +croisées : `Σ poids == 100` (base de renormalisation documentée) ET tout module +d'archétype `generator` porte EXACTEMENT `len(criteria)` `checks` (« generator = tous +les critères » — 18 modules generator × 5 checks vérifiés). Une **compensation** de +poids (+5 / −5) laisserait `Σ == 100` juste et deux lignes fausses → d'où le contrôle +**par critère**, pas seulement de la somme. + +### Preuve (4 morsures vérifiées + restauration verte) + +| Injection | Résultat | +|---|---| +| README prose 5 critères → 6 (poids table intacts) | ✗ dit 6 MAIS criteria en compte 5 · exit 1 | +| README poids `TESTS` 25 → 30 (compte intact) | ✗ dit poids 30 MAIS criteria dit 25 · exit 1 | +| README archétype `generator` (5→4 critères) | ✗ dit 4 MAIS generator applique les 5 · exit 1 | +| README claim prose reformulé (chiffre disparu) | ✗ « N critères 4Big » INTROUVABLE · exit 1 | +| restauré | ✓ 5 critères · poids 20/20/25/15/20 · Σ=100 · 18 gen×5 == artefact · exit 0 | + +- **`ci/README.md`** — surface ajoutée à la table récap du pipeline + paragraphe + descriptif (classe « barème auditeur 4Big »). + +Suite CI complète re-verte (7 gates) : `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_130055` · Buffer S8 · Domaine QA/Recette : le couple « **8 livrables de sprint · 7 métriques succès MVP** » du module d'acceptation (`qa/acceptance`) était transcrit À LA MAIN dans DEUX docs de module (README du module + fiche agent QA) sans AUCUN gate — le bloc racine ne gate QUE le README d'entrée **Tâche** : **Sprint 8 · buffer L75** (DevOps CI/CD · QA régression). Roadmap diff --git a/ci/README.md b/ci/README.md index 2baae98..1d1b06e 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) **et** du run-sheet de démo (nombre de modules cités dans le diagramme du README) **et** du montage Chat OTOIA par portail (`Custom Block`/configs runtime/portails, tous = 5 aujourd'hui, ×2 docs) **et** du module de recette `qa/acceptance` (couple livrables de sprint + métriques succès MVP, ×2 docs de module au-delà du README d'entrée) == 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) **et** du montage Chat OTOIA par portail (`Custom Block`/configs runtime/portails, tous = 5 aujourd'hui, ×2 docs) **et** du module de recette `qa/acceptance` (couple livrables de sprint + métriques succès MVP, ×2 docs de module au-delà du README d'entrée) **et** du barème de l'auditeur 4Big (nombre de critères en prose + poids par critère de la table + « generator (N critères) », recomputés de `quality_report.criteria`) == 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 @@ -289,6 +289,28 @@ comparé aux deux docs. Cohérence croisée en bonus : `MANIFEST.counts.{sprint_deliverables,mvp_metrics}` == le compte de la matrice qu'il résume — mord un compteur du manifeste désynchronisé de sa liste. +Même classe enfin sur le **barème de l'auditeur 4Big** (livrable `qa/audit_4big`). +Ironie déjà notée : le module qui NOTE les autres retranscrit son propre référentiel +de scoring **à la main** dans son README (`05_deliverables_mvp/qa/audit_4big/README.md`) +— la prose « Note chaque module sur **N critères 4Big déterministes** », la table +« Critère | Poids | … » (une ligne `id`→poids par critère) et l'archétype +« `generator` (**N** critères) ». Ces trois surfaces DÉRIVENT de +`quality_report.json.criteria` (byte-gaté par `check_artifacts`, recomputé du +référentiel à chaque build) ; ajouter/retirer un critère ou changer un poids ferait +dériver le README en silence pendant que l'artefact dit autre chose — dérive qu'aucune +suite `tests/` (qui teste des FONCTIONS, pas la prose) n'attrape. Le bloc racine ne +gate que la ligne « Résultat courant » (verdict/modules), jamais le barème. On +recompute donc le **nombre de critères** (prose + « generator »), et **chaque poids** +depuis `criteria[].weight` (zéro duplication du barème du générateur · #6). Cohérences +croisées : `Σ poids == 100` (base de renormalisation documentée) ET tout module +d'archétype `generator` porte EXACTEMENT `len(criteria)` `checks` — c.-à-d. +« generator = tous les critères », ce qui rend le « generator (N critères) » du README +vérifiablement vrai (mord une dérive du modèle qui le rendrait faux). Une **compensation** +(un poids +5 / un autre −5) laisserait `Σ == 100` juste et deux lignes fausses : d'où +le contrôle **par critère**, pas seulement de la somme. 4 morsures vérifiées : dérive +prose `5→6` (poids intacts) · dérive poids `TESTS 25→30` (compte intact) · dérive +`generator (5→4)` · claim prose disparu ; restauré = vert. + 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 09f76e5..8889937 100755 --- a/ci/check_readme_claims.sh +++ b/ci/check_readme_claims.sh @@ -301,6 +301,84 @@ else: bad(f"README audit_4big · {A4} dit {'/'.join(ma4.groups())} MAIS quality_report " f"dit {'/'.join(a4_exp)} ({a4_src}) → régénérer le README") +# ============================================================================ +# README de l'auditeur 4Big · BARÈME de notation (N critères + poids par critère) +# — même classe de « vert trompeur », au grain du référentiel de scoring. +# ---------------------------------------------------------------------------- +# Le barème 4Big est DATA-DERIVED : quality_report.json.criteria (byte-gaté par +# check_artifacts, RECOMPUTÉ du référentiel à chaque build) porte la liste des +# critères et leur poids. Le README les RETRANSCRIT À LA MAIN à trois endroits : +# • la prose « Note chaque module sur N critères 4Big déterministes », +# • la table « Critère | Poids | Fait vérifié » (une ligne id→poids par critère), +# • l'archétype « generator (N critères) » (generator applique TOUS les critères). +# Ajouter/retirer un critère ou changer un poids ferait dériver ces trois surfaces +# en silence pendant que l'artefact dit autre chose — aucune suite tests/ (qui teste +# des FONCTIONS, pas la prose du README) ne l'attrape. On RECOMPUTE ici chaque nombre +# depuis `criteria` (zéro duplication du barème du générateur · #6) et on exige +# l'égalité avec ce que la doc ÉCRIT. Cohérences croisées : Σ poids == 100 (base de +# renormalisation) ET tout module d'archétype `generator` porte EXACTEMENT +# len(criteria) checks — c.-à-d. « generator = tous les critères » (mord une dérive +# du modèle qui rendrait le « generator (N critères) » du README faux). Un claim +# absent échoue AUSSI (disparition = régression de traçabilité). +# ============================================================================ +try: + qr_crit = load("qa/audit_4big/out/quality_report.json") + criteria = qr_crit["criteria"] +except (OSError, KeyError, ValueError) as e: + bad(f"README audit_4big · quality_report illisible : {e}"); criteria = None +if criteria is not None and a4_txt: + n_crit = len(criteria) + # 1) prose « N critères 4Big déterministes » + mc = re.search(r"sur (\d+) critères 4Big déterministes", a4_txt) + if mc is None: + bad(f"README audit_4big · prose « N critères 4Big » — claim INTROUVABLE dans " + f"{A4} (attendu {n_crit})") + elif mc.group(1) == str(n_crit): + good(f"README audit_4big · prose critères — {mc.group(1)} == criteria ({n_crit})") + else: + bad(f"README audit_4big · prose critères — {A4} dit {mc.group(1)} MAIS criteria " + f"en compte {n_crit} → régénérer le README") + # 2) table « Critère | Poids | … » — une ligne id→poids par critère + for c in criteria: + cid, w = c["id"], str(c["weight"]) + mw = re.search(rf"\| `{re.escape(cid)}` \| (\d+) \|", a4_txt) + if mw is None: + bad(f"README audit_4big · critère {cid} — ligne INTROUVABLE dans {A4} " + f"(attendu poids={w})") + elif mw.group(1) == w: + good(f"README audit_4big · critère {cid} — poids {mw.group(1)} == criteria ({w})") + else: + bad(f"README audit_4big · critère {cid} — {A4} dit poids {mw.group(1)} MAIS " + f"criteria dit {w} → régénérer le README") + # 3) archétype « generator (N critères) » — generator applique TOUS les critères + mg = re.search(r"`generator` \((\d+) critères\)", a4_txt) + if mg is None: + bad(f"README audit_4big · archétype « generator (N critères) » — claim " + f"INTROUVABLE dans {A4} (attendu {n_crit})") + elif mg.group(1) == str(n_crit): + good(f"README audit_4big · generator — {mg.group(1)} critères == criteria ({n_crit})") + else: + bad(f"README audit_4big · generator — {A4} dit {mg.group(1)} MAIS l'archétype " + f"generator applique les {n_crit} critères → régénérer le README") + # 4) cohérence croisée : Σ poids == 100 (base de renormalisation) + w_sum = sum(c["weight"] for c in criteria) + if w_sum == 100: + good(f"README audit_4big · Σ poids critères == {w_sum} (base 100 cohérente)") + else: + bad(f"README audit_4big · Σ poids critères == {w_sum} ≠ 100 — barème incohérent " + f"avec la renormalisation documentée") + # 5) cohérence croisée : tout module `generator` porte len(criteria) checks + gen = [m for m in qr_crit["modules"] if m.get("archetype") == "generator"] + gen_bad = [m["id"] for m in gen if len(m.get("checks", [])) != n_crit] + if not gen: + good("README audit_4big · aucun module d'archétype generator (rien à recouper)") + elif gen_bad: + bad(f"README audit_4big · modules generator avec ≠ {n_crit} checks : {gen_bad} " + f"→ « generator = tous les critères » violé") + else: + good(f"README audit_4big · {len(gen)} module(s) generator portent {n_crit} " + f"checks (« generator = tous les critères » vérifié)") + # ============================================================================ # Fiches agents · comptes de tests PAR SUITE (colonne « Tests » des tables) — # même classe de « vert trompeur », au grain le PLUS FIN.