diff --git a/05_activity_log/2026-07-31.md b/05_activity_log/2026-07-31.md index 8b40cf8..1cb5c98 100644 --- a/05_activity_log/2026-07-31.md +++ b/05_activity_log/2026-07-31.md @@ -1,5 +1,63 @@ # Activity Log · 2026-07-31 · Claude Code DTP +## Session `20260731_143104` · Buffer S8 · Domaine QA/Audit 5D (2e surface) : la **table « Verdict courant »** de l'audit de conformité énumérait À LA MAIN ses **4 open items** (`control → dimension → propriétaire`) sans gate d'IDENTITÉ — le bloc existant ne recompute que le COMPTE (« 4 à confirmer »), aveugle à un **échange** d'open item. + +**Tâche** : **Sprint 8 · buffer L75** (DevOps CI/CD · QA). Roadmap fonctionnellement +close ; poursuite de la série anti-dérive (CLAUDE.md #6). Le bloc `audit_5d` gaté +précédemment couvre « 17 contrôles / 5 dimensions » (×2 docs) **et** la ventilation +du verdict « **13 PASS · 0 FAIL · 4 à confirmer** » (recomputée d'`audit_report.totals`). +Restait, dans le **même README**, une surface data-derived distincte : la **table +« Verdict courant »** qui énumère les 4 contrôles ouverts avec leur dimension et leur +propriétaire. + +**Dérive silencieuse fermée** : +- `05_deliverables_mvp/qa/audit_5d/README.md:51-56` énumère 4 lignes + `| D1.1 · … | D1 | Direction |` … — chacune porte trois faits data-derived : + l'`id` du contrôle, sa **dimension**, son **propriétaire**. +- Source faisant autorité : `qa/audit_5d/out/audit_report.json.open_items[]` + (byte-gaté par `check_artifacts`, chaque item = `{control, dimension, owner, detail}` + recalculé en rejouant les contrôles sur les hand-off amont). +- Le bloc de **ventilation** ne gate QUE le **compte** (« 4 à confirmer »). Un + **échange** d'open item (ex. `D2.3 → D3.1`) laisse le compte à 4 — le compteur + reste AVEUGLE — pendant que l'artefact dit autre chose ; idem une **dimension** + mal étiquetée (`D1.3` rangé sous `D2`) ou un **propriétaire** réattribué. « Vert + trompeur » qu'aucune suite `tests/` (qui teste des FONCTIONS, pas la prose) n'attrape. +- **État courant** : **aucun open item périmé** — les 4 lignes recoupent l'artefact + exactement (anti-invention #6, rien à réécrire). Le défaut est la **surface ungated**. + +**Nouveau bloc dans `ci/check_readme_claims.sh`** : recompute l'ensemble +`{control:(dimension, propriétaire)}` depuis `audit_report.open_items` (zéro +duplication du modèle de l'auditeur · #6) et exige que la table README l'énumère +**exactement** : contrôle par **ENSEMBLE** (absent ET en trop mordus, pas seulement +présence), puis `(dimension, propriétaire)` **par LIGNE**. Même patron que +l'énumération des confirmations DevOps ou la carte de renormalisation par archétype +4Big. Cohérence croisée en bonus : l'ensemble des `control` == `MANIFEST.open_items` +(le manifeste qui résume le rapport) — mord un manifeste désynchronisé de son rapport. + +### Preuve (4 morsures vérifiées + restauration verte) + +| Injection | Résultat | +|---|---| +| échange `D2.3 → D3.1` (compte reste 4 · gate-compte aveugle) | ✗ énumère `[…,D3.1]` MAIS référentiel `[…,D2.3]` · exit 1 | +| dimension `D1.3` étiquetée `D2` (compte + ids intacts) | ✗ open item D1.3 dit dimension=D2 MAIS artefact D1 · exit 1 | +| propriétaire `D1.1` `Direction → Compta` (compte + ids + dims intacts) | ✗ D1.1 dit propriétaire=Compta MAIS artefact Direction · exit 1 | +| ligne `D1.2` supprimée (sous-ensemble) | ✗ énumère `[D1.1,D1.3,D2.3]` MAIS référentiel `[D1.1,D1.2,D1.3,D2.3]` · exit 1 | +| restauré | ✓ `[D1.1,D1.2,D1.3,D2.3]` (control→dimension→propriétaire) == `audit_report.open_items` · exit 0 | + +- **`ci/README.md`** — surface ajoutée à la table récap du pipeline + paragraphe + descriptif (2e surface du même README audit 5D). + +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 ; +édition **hors** `05_deliverables_mvp` ⇒ 0 dérive d'artefact). + +**Auto-score 4Big** : 96/100. + +--- + ## Session `20260731_140101` · Buffer S8 · Domaine QA/Audit 4Big (2e surface) : la **carte de renormalisation PAR ARCHÉTYPE** de l'auditeur (« `data_room` (sans HANDOFF) · `parser` (sans CONTRAT … ni HANDOFF) · `contract` (sans CLI ni HANDOFF) ») était transcrite À LA MAIN dans le README du module — la session précédente a gaté le barème (critères + poids + `generator (N critères)`) mais PAS les exclusions des trois autres archétypes. **Tâche** : **Sprint 8 · buffer L75** (DevOps CI/CD · QA). Roadmap fonctionnellement diff --git a/ci/README.md b/ci/README.md index 1369397..16d8f0e 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) **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`) **et** de la carte de renormalisation par archétype de l'auditeur 4Big (critères EXCLUS de `data_room`/`parser`/`contract`, recomputés des `checks` par archétype de `quality_report.modules`) == 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 + énumération des open items `control→dimension→propriétaire` recomputée de `audit_report.open_items`) **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`) **et** de la carte de renormalisation par archétype de l'auditeur 4Big (critères EXCLUS de `data_room`/`parser`/`contract`, recomputés des `checks` par archétype de `quality_report.modules`) == 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 @@ -233,6 +233,21 @@ contrôle en A_CONFIRMER (pass 13→12, à confirmer 4→5) là où le total de reste 17 et resterait aveugle. Cohérence croisée en bonus : `controls_total` du MANIFEST doit égaler `totals.controls` du rapport. +Deuxième surface du **même README** de l'audit 5D : la **table « Verdict courant »** +énumère **à la main** les 4 open items (`| D1.1 · … | D1 | Direction |` …), chacun +portant trois faits data-derived — l'`id` du contrôle, sa **dimension** et son +**propriétaire**. Le bloc de ventilation ci-dessus ne gate que le **compte** (« 4 à +confirmer ») : un **échange** d'open item (ex. `D2.3 → D3.1`), une **dimension** mal +étiquetée (`D1.3` rangé sous `D2`) ou un **propriétaire** réattribué laisserait le +compte à 4 et **passerait en silence**. On RECOMPUTE l'ensemble +`{control:(dimension, propriétaire)}` depuis `qa/audit_5d/out/audit_report.json` +(`open_items[]`, byte-gaté) et on exige que la table README l'énumère **exactement** : +contrôle par **ensemble** (absent **et** en trop mordus, pas seulement présence), +puis `(dimension, propriétaire)` **par ligne** — même patron que l'énumération des +confirmations DevOps ou la carte de renormalisation par archétype 4Big. Cohérence +croisée en bonus : l'ensemble des `control` == `MANIFEST.open_items` (le manifeste qui +résume le rapport) — mord un manifeste désynchronisé de son propre rapport. + Même classe enfin sur le **run-book de déploiement VPS** (livrable `devops/deploy_runbook`) : le compte de **phases** (fiche agent `devops` — « graphe de **7 phases ordonnées** » **et** « exécute les 7 phases sur le VPS ») **et** le diff --git a/ci/check_readme_claims.sh b/ci/check_readme_claims.sh index 575dc52..8b5fa3f 100755 --- a/ci/check_readme_claims.sh +++ b/ci/check_readme_claims.sh @@ -1024,6 +1024,70 @@ if a5_ctl is not None: bad(f"Audit 5D · {A5D_FI} — dit {'/'.join(m.groups())} MAIS artefact dit " f"{a5_ctl}/{a5_dim} ({a5_src}) → régénérer la fiche") +# ============================================================================ +# Audit 5D (2e surface du MÊME README) · ÉNUMÉRATION des open items du « Verdict +# courant » (control → dimension → propriétaire) — même classe de « vert trompeur ». +# ---------------------------------------------------------------------------- +# Le bloc précédent gate le COMPTE de la ventilation (« 13 PASS · 0 FAIL · 4 à +# confirmer », pass/fail/à confirmer recomputés d'audit_report.totals) mais PAS +# l'IDENTITÉ des 4 contrôles ouverts ni leur (dimension, propriétaire). La table +# « Verdict courant » du README (05_deliverables_mvp/qa/audit_5d/README.md) énumère +# ces 4 lignes À LA MAIN, alors que audit_report.open_items[] (byte-gaté par +# check_artifacts ⇒ faisant autorité, chaque item = {control, dimension, owner, +# detail} recalculé en rejouant les contrôles) fait foi. Un ÉCHANGE d'open item +# (ex. D2.3 → D3.1) laisse le compte à 4 — le bloc de ventilation reste AVEUGLE — +# pendant que l'artefact dit autre chose ; idem une dimension mal étiquetée +# (D1.3 rangé sous D2) ou un propriétaire réattribué. « Vert trompeur » qu'aucune +# suite tests/ (qui teste des FONCTIONS, pas la prose) n'attrape. On RECOMPUTE +# l'ensemble {control:(dimension, owner)} depuis open_items (zéro duplication du +# modèle de l'auditeur · #6) et on exige que la table README l'énumère EXACTEMENT : +# contrôle par ENSEMBLE (absent ET en trop mordus, pas seulement présence), puis +# (dimension, propriétaire) par LIGNE. Même patron que l'énumération des +# confirmations DevOps ou la carte de renormalisation par archétype 4Big. Cohérence +# croisée en bonus : l'ensemble des `control` == MANIFEST.open_items (le manifeste +# qui résume le rapport) — mord un manifeste désynchronisé de son propre rapport. +# ============================================================================ +try: + a5_oi = load("qa/audit_5d/out/audit_report.json")["open_items"] + a5_oi_exp = {o["control"]: (o["dimension"], o["owner"]) for o in a5_oi} + a5_oi_man = set(load("qa/audit_5d/out/MANIFEST.json")["open_items"]) + if a5_oi_man != set(a5_oi_exp): + bad(f"Audit 5D · incohérence artefacts : MANIFEST open_items {sorted(a5_oi_man)} " + f"MAIS audit_report open_items {sorted(a5_oi_exp)}") +except (OSError, KeyError, ValueError) as e: + bad(f"Audit 5D · open_items illisible/inattendu : {e}"); a5_oi_exp = None +if a5_oi_exp is not None: + try: + a5_lines = open(f"{D}/qa/audit_5d/README.md", + encoding="utf-8").read().splitlines() + except OSError as e: + bad(f"Audit 5D · README illisible : {e}"); a5_lines = None + if a5_lines is not None: + # ligne de table = « | · | | | » + a5_row_re = re.compile( + r"^\|\s*(D\d+\.\d+)\b[^|]*\|\s*([^|]+?)\s*\|\s*([^|]+?)\s*\|\s*$") + a5_seen = {} + for ln in a5_lines: + m = a5_row_re.match(ln) + if m: + a5_seen[m.group(1)] = (m.group(2).strip(), m.group(3).strip()) + a5_exp_set, a5_got_set = set(a5_oi_exp), set(a5_seen) + if a5_got_set != a5_exp_set: + bad(f"Audit 5D · énumération open items — table README énumère " + f"{sorted(a5_got_set)} MAIS audit_report dit {sorted(a5_exp_set)} " + f"→ régénérer le README") + else: + a5_mism = [c for c in sorted(a5_exp_set) if a5_seen[c] != a5_oi_exp[c]] + if a5_mism: + for c in a5_mism: + bad(f"Audit 5D · open item {c} — README dit " + f"(dimension={a5_seen[c][0]}, propriétaire={a5_seen[c][1]}) MAIS " + f"audit_report dit (dimension={a5_oi_exp[c][0]}, " + f"propriétaire={a5_oi_exp[c][1]})") + else: + good(f"Audit 5D · énumération open items {sorted(a5_exp_set)} " + f"(control→dimension→propriétaire) == audit_report.open_items") + # ============================================================================ # DevOps · run-book de déploiement VPS (phases ordonnées / confirmations # préalables) — même classe de « vert trompeur », transcrite À LA MAIN dans