From 0d1df7a8dcfcf24140c9741a311288967308028c Mon Sep 17 00:00:00 2001 From: Claude Code DTP Worker Date: Fri, 31 Jul 2026 13:07:20 +0000 Subject: [PATCH] =?UTF-8?q?[DTP-Worker]=20Sprint=208=20=C2=B7=20buffer=20L?= =?UTF-8?q?75=20=C2=B7=20Domaine=20QA/Recette=20:=20le=20couple=20=C2=AB?= =?UTF-8?q?=208=20livrables=20de=20sprint=20=C2=B7=207=20m=C3=A9triques=20?= =?UTF-8?q?succ=C3=A8s=20MVP=20=C2=BB=20du=20module=20d'acceptation=20(qa/?= =?UTF-8?q?acceptance)=20=C3=A9tait=20transcrit=20=C3=80=20LA=20MAIN=20dan?= =?UTF-8?q?s=20DEUX=20docs=20de=20module=20(README=20qa/acceptance/README.?= =?UTF-8?q?md=20l.8-9=20+=20cellule=20fiche=20agent=20qa/AGENT.md=20l.27?= =?UTF-8?q?=20=C2=AB=208=20livrables=20+=207=20m=C3=A9triques=20=C2=BB)=20?= =?UTF-8?q?sans=20AUCUN=20gate=20=E2=80=94=20le=20bloc=20racine=20de=20che?= =?UTF-8?q?ck=5Freadme=5Fclaims=20ne=20recompute=20ce=20couple=20que=20pou?= =?UTF-8?q?r=20le=20SEUL=20README=20d'entr=C3=A9e=20(=C2=AB=20N=20promesse?= =?UTF-8?q?s=20(X=20sprint=20+=20Y=20m=C3=A9triques)=20=C2=BB),=20jamais?= =?UTF-8?q?=20pour=20ces=20deux=20docs=20de=20module=20qui=20en=20d=C3=A9r?= =?UTF-8?q?ivent.=20L'artefact=20qa/acceptance/out/acceptance=5Fmatrix.jso?= =?UTF-8?q?n=20(byte-gat=C3=A9=20par=20check=5Fartifacts)=20porte=20une=20?= =?UTF-8?q?ligne=20par=20promesse=20roadmap=20de=20kind=20sprint=5Fdeliver?= =?UTF-8?q?able=20OU=20mvp=5Fmetric=20;=20le=20roadmap-anchor=20(INV11)=20?= =?UTF-8?q?ancre=20les=20roadmap=5Fline=20de=20l'ARTEFACT=20au=20fichier?= =?UTF-8?q?=20roadmap=20mais=20ne=20compare=20PAS=20la=20prose=20des=20doc?= =?UTF-8?q?s.=20Ajouter=20une=20promesse=20roadmap=20=E2=86=92=20l'artefac?= =?UTF-8?q?t=20reb=C3=A2tit=209/7=20pendant=20que=20les=20deux=20docs=20re?= =?UTF-8?q?stent=20=C3=A0=208/7=20en=20silence=20(=C2=AB=20vert=20trompeur?= =?UTF-8?q?=20=C2=BB=20qu'aucune=20suite=20tests/,=20qui=20teste=20des=20F?= =?UTF-8?q?ONCTIONS=20pas=20la=20prose,=20n'attrape)=20=E2=86=92=20nouveau?= =?UTF-8?q?=20bloc=20dans=20check=5Freadme=5Fclaims=20recomputant=20CHAQUE?= =?UTF-8?q?=20nombre=20depuis=20SON=20PROPRE=20genre=20de=20la=20matrice?= =?UTF-8?q?=20(n=5Fsprint/n=5Fmetric=20d=C3=A9j=C3=A0=20d=C3=A9riv=C3=A9s?= =?UTF-8?q?=20=C2=B7=20z=C3=A9ro=20duplication=20#5)=20et=20exigeant=20l'?= =?UTF-8?q?=C3=A9galit=C3=A9=20avec=20les=20DEUX=20docs.=20Recomputer=20le?= =?UTF-8?q?s=20DEUX=20(sprint=20ET=20m=C3=A9triques)=20mord=20une=20d?= =?UTF-8?q?=C3=A9rive=20de=20l'un=20l=C3=A0=20o=C3=B9=20l'autre=20resterai?= =?UTF-8?q?t=20aveugle.=20Coh=C3=A9rence=20crois=C3=A9e=20en=20bonus=20:?= =?UTF-8?q?=20MANIFEST.counts.{sprint=5Fdeliverables,mvp=5Fmetrics}=20=3D?= =?UTF-8?q?=3D=20le=20compte=20de=20la=20matrice=20qu'il=20r=C3=A9sume=20?= =?UTF-8?q?=E2=80=94=20mord=20un=20compteur=20du=20manifeste=20d=C3=A9sync?= =?UTF-8?q?hronis=C3=A9=20de=20sa=20liste.=20Un=20claim=20absent=20=C3=A9c?= =?UTF-8?q?houe=20AUSSI=20(4=20morsures=20v=C3=A9rifi=C3=A9es=20:=20d?= =?UTF-8?q?=C3=A9rive=20sprint=20README=20capt=C3=A9e=20l=C3=A0=20o=C3=B9?= =?UTF-8?q?=20m=C3=A9triques=20reste=207=20=C2=B7=20d=C3=A9rive=20m=C3=A9t?= =?UTF-8?q?riques=20README=20capt=C3=A9e=20l=C3=A0=20o=C3=B9=20sprint=20re?= =?UTF-8?q?ste=208=20=C2=B7=20d=C3=A9rive=20fiche=20QA=20capt=C3=A9e=20l?= =?UTF-8?q?=C3=A0=20o=C3=B9=20le=20README=20est=20intact=20=C2=B7=20claim?= =?UTF-8?q?=20fiche=20disparu=20=3D=20red=20;=20restaur=C3=A9=20=3D=20gree?= =?UTF-8?q?n).=20=C3=89tat=20courant=20:=20aucun=20nombre=20p=C3=A9rim?= =?UTF-8?q?=C3=A9=20(8/7=20recoupe=20la=20matrice=20=C2=B7=20anti-inventio?= =?UTF-8?q?n=20#6,=20rien=20=C3=A0=20r=C3=A9=C3=A9crire)=20=E2=80=94=20le?= =?UTF-8?q?=20d=C3=A9faut=20est=20la=20surface=20ungated.=20ci/README.md?= =?UTF-8?q?=20(table=20+=20d=C3=A9tail)=20mis=20=C3=A0=20jour=20=C2=B7=207?= =?UTF-8?q?=20gates=20re-verts.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- 05_activity_log/2026-07-31.md | 60 +++++++++++++++++++++++++++++++ ci/README.md | 17 ++++++++- ci/check_readme_claims.sh | 68 +++++++++++++++++++++++++++++++++++ 3 files changed, 144 insertions(+), 1 deletion(-) diff --git a/05_activity_log/2026-07-31.md b/05_activity_log/2026-07-31.md index ffea241..7a0a07c 100644 --- a/05_activity_log/2026-07-31.md +++ b/05_activity_log/2026-07-31.md @@ -1,5 +1,65 @@ # Activity Log · 2026-07-31 · Claude Code DTP +## 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 +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 + audit 4big), DevOps +(run-book), Démo (run-sheet) et Frontend/Chat OTOIA. Cette session cible le **module de +recette** (`qa/acceptance`) — son couple data-derived « livrables de sprint / +métriques succès MVP », cité dans deux docs de module que le bloc racine ne couvrait pas. + +### Défaut trouvé — couple ungated, transcrit dans DEUX docs de module + +- **`05_deliverables_mvp/qa/acceptance/README.md:8-9`** : puces « **8 livrables de + sprint** (L33..L76) » et « **7 métriques succès MVP** (L81..L87) ». +- **`03_agents/qa/AGENT.md:27`** : cellule « mappe chaque promesse roadmap (**8 + livrables + 7 métriques**) vers sa preuve » — **même** couple dérivé de la **même** + matrice (source unique · #5). +- L'artefact source est byte-gaté par `check_artifacts` : + `qa/acceptance/out/acceptance_matrix.json` porte une ligne par promesse roadmap de + `kind` `sprint_deliverable` OU `mvp_metric`. Le bloc racine de `check_readme_claims` + recompute déjà ce couple pour le **SEUL README d'entrée** (« N promesses (X sprint + + Y métriques) ») — mais **jamais** ces deux docs de **module**. +- Le **roadmap-anchor** (INV11) ancre les `roadmap_line` de l'**ARTEFACT** au fichier + roadmap ; il ne compare **pas** la prose des docs. Ajouter une promesse roadmap → + l'artefact rebâtit 9/7 pendant que les deux docs restent à 8/7 en silence — « vert + trompeur » qu'aucune suite `tests/` (qui teste des FONCTIONS, pas la prose) n'attrape. +- **État courant** : **aucun nombre périmé** — 8/7 recoupe la matrice exactement + (anti-invention #6 : rien à réécrire). Le défaut est la **surface ungated**. + +**Nouveau bloc dans `ci/check_readme_claims.sh`** : recompute **chaque** nombre depuis +**son propre** genre de la matrice (`kind` `sprint_deliverable` / `mvp_metric`, +`n_sprint`/`n_metric` déjà dérivés plus haut · zéro duplication #5) et exige l'égalité +avec les DEUX docs. Recomputer les DEUX (sprint ET métriques) mord une dérive de l'un +là où l'autre resterait aveugle. 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. Un claim absent +échoue AUSSI (traçabilité). + +### Preuve (4 morsures vérifiées + restauration verte) + +| Injection | Résultat | +|---|---| +| README sprint `8→9` (métriques reste 7) | ✗ dit 9/7 MAIS matrice 8/7 · exit 1 | +| README métriques `7→6` (sprint reste 8) | ✗ dit 8/6 MAIS matrice 8/7 · exit 1 | +| fiche QA parenthèse `8→10` (README intact) | ✗ dit 10/7 MAIS matrice 8/7 · exit 1 | +| fiche QA claim supprimé (reformulé) | ✗ claim INTROUVABLE · exit 1 | +| restauré | ✓ 8/7 == matrice (2 docs) · exit 0 | + +- **`ci/README.md`** — surface ajoutée à la table récap du pipeline + paragraphe + descriptif (classe « module de recette `qa/acceptance` »). + +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_123055` · Buffer S8 · Domaine Frontend/Chat OTOIA : le montage de l'assistant conversationnel par portail (`frontend/chat_otoia`) affichait « **5 `Custom Block` + 5 configs runtime** » (fiche agent frontend_console) et « **5 portails · 5 fixtures · 5 configs runtime** » (README du module) — trois nombres data-derived, TOUS = 5 (piège aveugle), 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 498c4bb..2baae98 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) == 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) == 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 @@ -274,6 +274,21 @@ croisée : `custom_blocks` == `mounts` == `portails` == |`MANIFEST.portails`| (l bijection 1 block ⇔ 1 mount ⇔ 1 portail du générateur) — mord un compteur désynchronisé de sa propre liste. +Même classe enfin sur le **module de recette** (`qa/acceptance`) : le couple +« **N livrables de sprint** + **M métriques succès MVP** » est transcrit **à la +main** dans DEUX docs que le bloc racine ne couvre pas — le README du module +(deux puces) ET la fiche agent QA (« (N livrables + M métriques) »). Le bloc +racine ne gate que le SEUL README d'entrée (« N promesses (X sprint + Y +métriques) ») ; ces deux docs de module en dérivent. Ajouter une promesse roadmap +(l'artefact rebâtit 9/7) ferait dériver les deux docs en silence — piège que ni +les suites `tests/` ni le **roadmap-anchor** (qui ancre les `roadmap_line` de +l'ARTEFACT, pas la prose des docs) n'attrapent. Chaque nombre est recomputé depuis +**son propre** genre de la matrice `qa/acceptance/out/acceptance_matrix.json` +(`kind` `sprint_deliverable` / `mvp_metric`, byte-gatée par `check_artifacts`) et +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. + 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 c781e4f..09f76e5 100755 --- a/ci/check_readme_claims.sh +++ b/ci/check_readme_claims.sh @@ -79,6 +79,11 @@ # MANIFEST.counts (custom_blocks/mounts/portails, recomptés du contrat RBAC, # byte-gatés) + cohérence croisée == |MANIFEST.portails| (bijection 1 block ⇔ # 1 mount ⇔ 1 portail) +# qa/acceptance/README.md + 03_agents/qa/AGENT.md (module de recette) : +# • couple « N livrables de sprint + M métriques succès MVP » transcrit À LA MAIN +# dans DEUX docs de module (le bloc racine ne gate que le README d'entrée) +# → recompté depuis la matrice acceptance (kind sprint_deliverable / mvp_metric) +# + cohérence croisée == MANIFEST.counts (compteur ⇔ matrice résumée) # 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é). # @@ -1125,6 +1130,69 @@ if co_cb is not None: bad(f"Chat OTOIA · {CO_RD} — dit {m.group(1)} configs runtime MAIS artefact " f"dit {co_mo} → régénérer le README") +# ============================================================================ +# Recette (module) · couple « N livrables de sprint + M métriques succès MVP » +# transcrit À LA MAIN dans DEUX docs — même classe de « vert trompeur ». +# ---------------------------------------------------------------------------- +# La matrice d'acceptation (qa/acceptance/out/acceptance_matrix.json, byte-gatée +# par check_artifacts ⇒ faisant autorité) porte une ligne par promesse roadmap, +# de `kind` sprint_deliverable OU mvp_metric. Le compte de chaque genre (n_sprint, +# n_metric déjà recalculés plus haut depuis la matrice, jamais saisis) est repris +# À LA MAIN à DEUX endroits que le bloc racine ne couvre PAS : le README du module +# (05_deliverables_mvp/qa/acceptance/README.md — puces « N livrables de sprint » +# puis « M métriques succès MVP ») ET la fiche agent QA (03_agents/qa/AGENT.md — +# « (N livrables + M métriques) »). Le bloc racine plus haut ne gate que le SEUL +# README d'entrée (« N promesses (X sprint + Y métriques) ») ; ces deux docs de +# module en dérivent sans gate. Ajouter une promesse roadmap (l'artefact rebâtit +# 9/7) ferait dériver LES DEUX docs en silence pendant que la matrice dit autre +# chose — piège que ni les suites tests/ (qui testent des FONCTIONS, pas la prose) +# ni le roadmap-anchor (qui ancre les `roadmap_line` de l'ARTEFACT, pas la prose +# des docs) n'attrapent. On RECOMPUTE ici chaque nombre depuis SON PROPRE genre de +# la matrice (jamais figé · zéro duplication #5) et on exige l'égalité avec les +# DEUX docs. Recomputer les DEUX (sprint ET métriques) mord une dérive de l'un là +# où l'autre resterait aveugle. Cohérence croisée en bonus : +# MANIFEST.counts.{sprint_deliverables,mvp_metrics} == (n_sprint, n_metric) — mord +# un compteur du manifeste désynchronisé de la matrice qu'il résume. Un claim +# absent échoue AUSSI (la disparition d'un chiffre est une régression · traçabilité). +# ============================================================================ +ACC_RD = f"{D}/qa/acceptance/README.md" +ACC_FI = "03_agents/qa/AGENT.md" +acc_exp = (str(n_sprint), str(n_metric)) +acc_src = f"livrables_sprint={acc_exp[0]} métriques={acc_exp[1]}" +try: + acc_mc = load("qa/acceptance/out/MANIFEST.json")["counts"] + if (str(acc_mc["sprint_deliverables"]), str(acc_mc["mvp_metrics"])) != acc_exp: + bad(f"Recette · incohérence artefact : MANIFEST.counts dit " + f"{acc_mc['sprint_deliverables']}/{acc_mc['mvp_metrics']} MAIS la matrice " + f"dit {'/'.join(acc_exp)} (compteur désynchronisé de sa liste)") +except (OSError, KeyError, ValueError) as e: + bad(f"Recette · MANIFEST acceptance illisible/inattendu : {e}") +# 1) README du module — deux puces distinctes (livrables sprint · métriques MVP) +acc_rd = read_norm(ACC_RD) +if acc_rd is not None: + ms = re.search(r"\*\*(\d+) livrables de sprint\*\*", acc_rd) + mm = re.search(r"\*\*(\d+) métriques succès MVP\*\*", acc_rd) + if ms is None or mm is None: + bad(f"Recette · {ACC_RD} — puces « N livrables de sprint » / « M métriques " + f"succès MVP » INTROUVABLE(s) (attendu {acc_src})") + elif (ms.group(1), mm.group(1)) == acc_exp: + good(f"Recette · {ACC_RD} — {ms.group(1)}/{mm.group(1)} == matrice ({acc_src})") + else: + bad(f"Recette · {ACC_RD} — dit {ms.group(1)}/{mm.group(1)} MAIS matrice dit " + f"{'/'.join(acc_exp)} ({acc_src}) → régénérer le README") +# 2) Fiche agent QA — parenthèse « (N livrables + M métriques) » +acc_fi = read_norm(ACC_FI) +if acc_fi is not None: + m = re.search(r"\((\d+) livrables \+ (\d+) métriques\)", acc_fi) + if m is None: + bad(f"Recette · {ACC_FI} — claim « (N livrables + M métriques) » INTROUVABLE " + f"(attendu {acc_src})") + elif m.groups() == acc_exp: + good(f"Recette · {ACC_FI} — {'/'.join(m.groups())} == matrice ({acc_src})") + else: + bad(f"Recette · {ACC_FI} — dit {'/'.join(m.groups())} MAIS matrice dit " + f"{'/'.join(acc_exp)} ({acc_src}) → régénérer la fiche") + sys.exit(1 if FAIL else 0) PY rc=$?