From 8f27b65724100d7eac30ee683ef69a44f826fbf0 Mon Sep 17 00:00:00 2001 From: Claude Code DTP Worker Date: Fri, 31 Jul 2026 10:35:56 +0000 Subject: [PATCH] =?UTF-8?q?[DTP-Worker]=20Sprint=208=20=C2=B7=20buffer=20L?= =?UTF-8?q?75=20=C2=B7=20Domaine=20Legal=20:=20la=20synth=C3=A8se=20du=20D?= =?UTF-8?q?ocType=20=C2=AB=20CONFOTUR=20Application=20=C2=BB=20(=C2=AB=201?= =?UTF-8?q?8=20champs=20=C2=B7=2014=20de=20donn=C3=A9e=20=C2=B7=204=20sect?= =?UTF-8?q?ions=20=C2=B7=203=20r=C3=B4les=20=C2=B7=202=20=C3=A9v=C3=A8neme?= =?UTF-8?q?nts=20de=20d=C3=A9p=C3=B4t=20=C2=BB)=20=C3=A9tait=20transcrite?= =?UTF-8?q?=20=C3=80=20LA=20MAIN=20dans=20DEUX=20docs=20(README=20du=20mod?= =?UTF-8?q?ule=20legal/confotur=20+=20cellule=20de=20la=20fiche=20agent=20?= =?UTF-8?q?onapi=5Flegal,=20m=C3=AAmes=205=20nombres=20du=20m=C3=AAme=20sp?= =?UTF-8?q?ec)=20sans=20AUCUN=20gate=20=E2=80=94=20ajouter=20un=20champ/se?= =?UTF-8?q?ction/r=C3=B4le=20au=20confotur=5Fspec=20ferait=20d=C3=A9river?= =?UTF-8?q?=20les=20deux=20docs=20en=20silence=20pendant=20que=20legal/con?= =?UTF-8?q?fotur/out/MANIFEST.json=20(byte-gat=C3=A9=20par=20check=5Fartif?= =?UTF-8?q?acts,=20counts=20recompt=C3=A9s=20du=20confotur=5Fspec.json=20?= =?UTF-8?q?=C3=A0=20chaque=20build)=20dit=20autre=20chose=20=E2=86=92=20no?= =?UTF-8?q?uveau=20bloc=20dans=20check=5Freadme=5Fclaims=20recomputant=20l?= =?UTF-8?q?e=20quintuplet=20(fields=20+=20data=5Ffields=20+=20sections=20+?= =?UTF-8?q?=20roles=20+=20depot=5Fevents)=20depuis=20MANIFEST.counts=20et?= =?UTF-8?q?=20exigeant=20l'=C3=A9galit=C3=A9=20avec=20les=20deux=20docs=20?= =?UTF-8?q?(4=20morsures=20v=C3=A9rifi=C3=A9es=20:=20d=C3=A9rive=20section?= =?UTF-8?q?s=20README=20=C2=B7=20d=C3=A9rive=20champs=202e=20doc=20=C2=B7?= =?UTF-8?q?=20d=C3=A9rive=20=C3=A9v=C3=A8nements=20de=20d=C3=A9p=C3=B4t=20?= =?UTF-8?q?capt=C3=A9e=20l=C3=A0=20o=C3=B9=20le=20total=20de=20champs=20es?= =?UTF-8?q?t=20aveugle=20=C2=B7=20claim=20disparu=20=3D=20red)?= 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 | 66 +++++++++++++++++++++++++++++++++++ ci/README.md | 13 ++++++- ci/check_readme_claims.sh | 62 ++++++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+), 1 deletion(-) diff --git a/05_activity_log/2026-07-31.md b/05_activity_log/2026-07-31.md index 9278e89..b7bc373 100644 --- a/05_activity_log/2026-07-31.md +++ b/05_activity_log/2026-07-31.md @@ -1,5 +1,71 @@ # Activity Log · 2026-07-31 · Claude Code DTP +## Session `20260731_103044` · Buffer S8 · Domaine Legal : la synthèse du DocType « CONFOTUR Application » (« 18 champs · 14 de donnée · 4 sections · 3 rôles · 2 évènements de dépôt ») était transcrite À LA MAIN dans DEUX docs (README du module legal/confotur + fiche agent onapi_legal) sans AUCUN gate — ajouter un champ/section/rôle au spec ferait dériver les deux docs en silence + +**Tâche** : **Sprint 8 · buffer L75** (DevOps CI/CD · QA régression). Roadmap +fonctionnellement close (22/22 modules · 564 tests · 7 gates verts au démarrage). +Poursuite de la série anti-dérive (CLAUDE.md #6). Les sessions précédentes ont gaté +les comptes data-derived des domaines tests, portails, RBAC (3 volets), SEO, CRM +(pipeline vente) et mobile. Cette session cible le **domaine Legal** — la **structure +du DocType CONFOTUR**, dont un quintuplet data-derived cité dans deux docs pointait +l'artefact généré sans être recomputé par aucun gate. + +### Défaut trouvé — comptes du DocType ungated, transcrits dans DEUX docs + +- **`05_deliverables_mvp/legal/confotur/README.md:17`** : ligne de synthèse + « **18 champs (14 de donnée) · 4 sections · 3 rôles · soumissible · 2 évènements + de dépôt** ». +- **`03_agents/onapi_legal/AGENT.md:28`** : cellule de table reprenant les **mêmes** + 5 nombres (chacun en gras), **dérivés du même spec** (source unique · #5). +- **Pourquoi non gaté** : `check_readme_claims` couvrait tests, portails, RBAC, SEO, + CRM pipeline et mobile — jamais la structure du DocType CONFOTUR. `check_docs` ne + valide que les liens ; `check_artifacts` prouve que + `legal/confotur/out/MANIFEST.json` est **reproductible byte-for-byte** (bloc `counts` + = {fields, data_fields, sections, permissions, roles, depot_events} recomptés de + `confotur_spec.json` à chaque build) mais **ne compare pas** ce que les DEUX docs en + **écrivent**. Ajouter un champ/section/rôle ferait dériver les deux docs **en + silence** — même « vert trompeur ». +- **État courant** : **aucun nombre périmé** — 18/14/4/3/2 recoupe MANIFEST + exactement (anti-invention #6 : rien à réécrire). Le défaut est la **surface + ungated** (faux-vert latent), pas un chiffre faux. + +### Fix — extension de `check_readme_claims.sh` (recompute les 5 volets depuis MANIFEST) + +Nouveau bloc qui RECOMPUTE le quintuplet `(fields, data_fields, sections, roles, +depot_events)` depuis `legal/confotur/out/MANIFEST.counts` (source déjà byte-gatée · +zéro duplication du compteur du générateur) et exige l'égalité avec **les deux** docs : +la ligne du README (sans gras) **et** la cellule de la fiche onapi_legal (chaque +nombre en gras). Recomputer **les 5** volets (et pas le seul total de champs) mord une +dérive de N'IMPORTE lequel là où un seul nombre resterait aveugle. Un claim **absent** +échoue AUSSI. En-tête du gate + `ci/README.md` (§ table + § détail) mis à jour. + +### Preuves de morsure + +| Injection | Résultat | +|---|---| +| **README** sections `4→5` | **exit 1** `dit 18/14/5/3/2 MAIS MANIFEST dit 18/14/4/3/2` | +| **Fiche** champs `18→19` (README intact) | **exit 1** `dit 19/14/4/3/2 MAIS MANIFEST 18/14/4/3/2` — mord la 2e doc | +| **Fiche** évènements dépôt `2→3` | **exit 1** `dit 18/14/4/3/3 MAIS MANIFEST 18/14/4/3/2` — mord le dernier volet là où le total de champs est aveugle | +| **Claim disparu** (README reformulé) | **exit 1** `synthèse DocType INTROUVABLE (attendu champs=18 …)` | +| Restauré | **exit 0** (2 docs verts) | + +### Vérifs + +| Contrôle | Résultat | +|---|---| +| 7 gates (`guard`/`json`/`docs`/`artifacts`/`regression`/`ci_integrity`/`readme_claims`) | ✅ exit 0 | +| `check_artifacts` (rebuild + égalité byte des `out/`) | ✅ (édition **hors** `05_deliverables_mvp` ⇒ 0 dérive d'artefact) | +| Matrice de régression | **564/22 · PASS** inchangée (ni `tests/` ni doc-evidence touchés) | +| Diff | 2 fichiers (`ci/check_readme_claims.sh` + `ci/README.md`) · 0 untracked | + +**Anti-invention (#6)** : rien inventé — le gate recompute les 5 volets depuis +`confotur MANIFEST.counts` au lieu de figer un compteur ; les nombres des deux docs +**découlent** de l'artefact. +**Hors périmètre worker (VPS · #8)** : néant (gate python/stdlib en-repo). +**Auto-score 4Big** : 96/100. + +--- + ## Session `20260731_093041` · Buffer S8 · Domaine CRM (non couvert hors prose « tests ») : le pipeline vente « 9 états · 11 transitions » était transcrit À LA MAIN dans DEUX README de module (titre workflow_vente + « 9 états du pipeline » de dossier_vente, même compte du même graphe) sans AUCUN gate — ajouter un état ferait dériver les deux docs en silence **Tâche** : **Sprint 8 · buffer L75** (DevOps CI/CD · QA régression). Roadmap diff --git a/ci/README.md b/ci/README.md index b4020f9..8679bca 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) == 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) == 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 @@ -209,6 +209,17 @@ identifiants_a_confirmer}`, byte-gaté par `check_artifacts`). Recomputer **les quatre** volets mord une dérive de n'importe lequel (ajouter un rôle/portail RBAC, une langue, un credential store) là où un seul nombre resterait aveugle. +Même classe enfin sur le **DocType CONFOTUR Application** (livrable `legal/confotur`) : +la ligne de synthèse du README du module (« **18 champs** (14 de donnée) · **4 +sections** · **3 rôles** · … · **2 évènements de dépôt** ») **et** la cellule de la +fiche agent `onapi_legal` transcrivaient **à la main** le même quintuplet, dérivé du +spec du DocType. Les cinq nombres sont recomputés depuis +`legal/confotur/out/MANIFEST.json` (`counts.{fields,data_fields,sections,roles, +depot_events}`, recomptés du `confotur_spec.json` à chaque build, byte-gaté par +`check_artifacts`) et comparés aux **deux** docs. Recomputer **les cinq** volets mord +une dérive de n'importe lequel (ajouter un champ/une section/un rôle au spec) là où un +seul nombre resterait aveugle. + 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 22c38bd..3dda7a6 100755 --- a/ci/check_readme_claims.sh +++ b/ci/check_readme_claims.sh @@ -67,6 +67,11 @@ # a_confirmer » transcrit À LA MAIN → app_config # MANIFEST.counts (onglets/roles_couverts/langues/identifiants_a_confirmer, # recomptés de rbac_50_roles + seo_spec + a_confirmer, byte-gatés) +# legal/confotur/README.md + 03_agents/onapi_legal/AGENT.md (DocType CONFOTUR) : +# • quintuplet « 18 champs (14 de donnée) · 4 sections · 3 rôles · 2 évènements +# de dépôt » transcrit À LA MAIN dans DEUX docs → confotur +# MANIFEST.counts (fields/data_fields/sections/roles/depot_events, recomptés +# de confotur_spec.json, byte-gatés) # 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é). # @@ -740,6 +745,63 @@ if mob_exp is not None: bad(f"Mobile · {MOB_RD} — dit {'/'.join(m.groups())} MAIS MANIFEST dit " f"{'/'.join(mob_exp)} ({mob_src}) → régénérer le README") +# ============================================================================ +# Legal · DocType CONFOTUR Application (champs / sections / rôles / évènements de +# dépôt) — même classe de « vert trompeur », transcrit À LA MAIN dans DEUX docs. +# ---------------------------------------------------------------------------- +# Le générateur confotur émet out/MANIFEST.json dont `counts` porte fields, +# data_fields, sections, roles et depot_events, RECOMPTÉS à chaque build depuis +# confotur_spec.json (byte-gaté par check_artifacts ⇒ artefact faisant autorité). +# Ce quintuplet est recopié À LA MAIN à DEUX endroits : la ligne de synthèse du +# README du module (05_deliverables_mvp/legal/confotur/README.md — « 18 champs +# (14 de donnée) · 4 sections · 3 rôles · … · 2 évènements de dépôt ») ET la +# cellule de la fiche agent onapi_legal (03_agents/onapi_legal/AGENT.md — mêmes 5 +# nombres, chacun en gras). Aucun gate ne les couvrait : ajouter un champ/une +# section/un rôle au spec ferait dériver LES DEUX docs en silence pendant que +# l'artefact dit autre chose — même piège que les mots-clés SEO ou les triplets +# par workspace des portails. On RECOMPUTE ici les 5 nombres depuis +# MANIFEST.counts (déjà byte-gaté · zéro duplication du compteur du générateur) +# et on exige l'égalité avec les DEUX docs. Un claim absent échoue AUSSI +# (traçabilité). Recomputer les 5 (et pas le seul total de champs) mord une dérive +# de N'IMPORTE lequel des volets là où un seul nombre resterait aveugle. +# ============================================================================ +CONF_RD = f"{D}/legal/confotur/README.md" +CONF_FI = "03_agents/onapi_legal/AGENT.md" +try: + conf_c = load("legal/confotur/out/MANIFEST.json")["counts"] + conf_exp = (str(conf_c["fields"]), + str(conf_c["data_fields"]), + str(conf_c["sections"]), + str(conf_c["roles"]), + str(conf_c["depot_events"])) +except (OSError, KeyError, ValueError) as e: + bad(f"Legal confotur · MANIFEST illisible/inattendu : {e}"); conf_exp = None +if conf_exp is not None: + conf_src = (f"champs={conf_exp[0]} données={conf_exp[1]} sections={conf_exp[2]} " + f"rôles={conf_exp[3]} dépôts={conf_exp[4]}") + # README du module (sans gras) et fiche agent (chaque nombre en gras) — + # même quintuplet, deux mises en forme distinctes du même artefact. + conf_docs = ( + (CONF_RD, r"(\d+) champs \((\d+) de donnée\) · (\d+) sections · (\d+) rôles" + r" · soumissible · (\d+) évènements de dépôt"), + (CONF_FI, r"\*\*(\d+) champs\*\* \((\d+) de donnée\) · \*\*(\d+) sections\*\*" + r" · \*\*(\d+) rôles/permissions\*\* · \*\*(\d+) évènements de dépôt\*\*"), + ) + for path_f, pat in conf_docs: + txt = read_norm(path_f) + if txt is None: + continue + m = re.search(pat, txt) + if m is None: + bad(f"Legal confotur · {path_f} — synthèse DocType INTROUVABLE " + f"(attendu {conf_src})") + elif m.groups() == conf_exp: + good(f"Legal confotur · {path_f} — {'/'.join(m.groups())} == " + f"MANIFEST ({conf_src})") + else: + bad(f"Legal confotur · {path_f} — dit {'/'.join(m.groups())} MAIS " + f"MANIFEST dit {'/'.join(conf_exp)} ({conf_src}) → régénérer le doc") + sys.exit(1 if FAIL else 0) PY rc=$?