From 56e64d60895ba3bc0ad32e2197598ea39a2c23fd Mon Sep 17 00:00:00 2001 From: Claude Code DTP Worker Date: Fri, 31 Jul 2026 08:06:10 +0000 Subject: [PATCH] =?UTF-8?q?[DTP-Worker]=20Sprint=208=20=C2=B7=20buffer=20L?= =?UTF-8?q?75=20=C2=B7=20Hors=20du=20domaine=20=C2=AB=20tests=20=C2=BB=20:?= =?UTF-8?q?=20les=20comptes=20PAR=20WORKSPACE=20(cartes/liens/r=C3=B4les)?= =?UTF-8?q?=20des=205=20portails=20frontend=20=C3=A9taient=20transcrits=20?= =?UTF-8?q?=C3=80=20LA=20MAIN=20dans=20DEUX=20docs=20(table=20du=20README?= =?UTF-8?q?=20module=20+=20prose=20de=20la=20fiche=20frontend=5Fconsole)?= =?UTF-8?q?=20sans=20AUCUN=20gate=20=E2=80=94=20ajouter=20un=20r=C3=B4le/D?= =?UTF-8?q?ocType=20au=20contrat=20RBAC=20ferait=20d=C3=A9river=20les=20de?= =?UTF-8?q?ux=20docs=20en=20silence=20pendant=20que=20frontend/portails/ou?= =?UTF-8?q?t/MANIFEST.json=20(byte-gat=C3=A9=20par=20check=5Fartifacts)=20?= =?UTF-8?q?dit=20autre=20chose=20=E2=86=92=20nouveau=20bloc=20dans=20check?= =?UTF-8?q?=5Freadme=5Fclaims=20recomputant=20chaque=20triplet=20+=20le=20?= =?UTF-8?q?total=20=C2=AB=20N=20Workspace=20natifs=20=C2=BB=20depuis=20MAN?= =?UTF-8?q?IFEST.workspaces=20et=20exigeant=20l'=C3=A9galit=C3=A9=20avec?= =?UTF-8?q?=20les=20deux=20docs=20(4=20morsures=20:=20d=C3=A9rive=20table?= =?UTF-8?q?=20=C2=B7=20d=C3=A9rive=20prose=20capt=C3=A9e=20l=C3=A0=20o?= =?UTF-8?q?=C3=B9=20la=20table=20est=20aveugle=20=C2=B7=20cellule=20dispar?= =?UTF-8?q?ue=20=C2=B7=20total)?= 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 | 68 +++++++++++++++++++++++++++++++ ci/README.md | 11 ++++- ci/check_readme_claims.sh | 77 +++++++++++++++++++++++++++++++++++ 3 files changed, 155 insertions(+), 1 deletion(-) diff --git a/05_activity_log/2026-07-31.md b/05_activity_log/2026-07-31.md index 45ecce3..b1dd7cf 100644 --- a/05_activity_log/2026-07-31.md +++ b/05_activity_log/2026-07-31.md @@ -1,5 +1,73 @@ # Activity Log · 2026-07-31 · Claude Code DTP +## Session `20260731_080025` · Buffer S8 · Hors du domaine « tests » : les comptes PAR WORKSPACE (cartes/liens/rôles) des 5 portails frontend, transcrits À LA MAIN dans DEUX docs (table du README module + prose de la fiche agent), n'étaient couverts par AUCUN gate — ajouter un rôle/DocType au contrat RBAC 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 du domaine **tests** (sommes, cellules par-suite, agrégats en prose). Cette +session sort de ce domaine : audit des comptes **data-derived** cités dans les fiches +et README de module qui pointent un artefact mais n'étaient recomputés par aucun gate. + +### Défaut trouvé — comptes par workspace ungated, transcrits dans DEUX docs + +- **`05_deliverables_mvp/frontend/portails/README.md:29-33`** : table + « Portail | Workspace | **Cartes** | **Liens** | **Rôles restreints** » — 5 lignes + × 3 nombres **saisis à la main**. +- **`03_agents/frontend_console/AGENT.md:28`** : prose « **5 `Workspace` natifs** + (`OTO Ventes` 4 cartes/11 liens/12 rôles · `OTO Construction` 4/9/10 · `OTO Achat` + 3/8/5 · `OTO Compta` 4/11/8 · `OTO Direction` 4/14/9) » — **mêmes** triplets recopiés. +- **Pourquoi non gaté** : `check_readme_claims` couvrait les comptes de **tests** + (README racine, fiches QA/Backend, cellules par-suite, prose CRM/RBAC/e-CF) et + `audit_4big`/`acceptance` — jamais les comptes **de structure** des portails. + `check_docs` ne valide que les liens ; `check_artifacts` prouve que + `frontend/portails/out/MANIFEST.json` est **reproductible byte-for-byte** (tableau + `workspaces` = {nb_cartes, nb_liens, nb_roles} recalculés depuis le contrat RBAC à + chaque build) mais **ne compare pas** ce que les DEUX docs en **écrivent**. Ajouter + un rôle/DocType au contrat ferait dériver les deux docs **en silence** pendant que + l'artefact dit autre chose — même « vert trompeur » que les cellules « Tests ». +- **État courant** : **aucun nombre périmé** — les 15 triplets et le total « 5 » des + deux docs recoupent 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 par workspace depuis MANIFEST) + +Nouveau bloc qui, pour **chaque** workspace de `MANIFEST.workspaces` (source déjà +byte-gatée · zéro duplication du compteur du générateur), RECOMPUTE le triplet +`(nb_cartes, nb_liens, nb_roles)` et exige l'égalité avec **les deux** docs : la ligne +de table du README (ancrée `| | \`\` | C | L | R |`) **et** la cellule +de prose de la fiche (`\`\` C cartes/L liens/R rôles`, forme abrégée `C/L/R` +tolérée). + contrôle du **total** « N `Workspace` natifs » == len(workspaces). Un +workspace **absent** d'un doc échoue AUSSI. En-tête du gate + `ci/README.md` +(§ table + § détail) mis à jour. + +### Preuves de morsure + +| Injection | Résultat | +|---|---| +| **README table** `OTO Compta` liens `11→12` | **exit 1** `README dit 4/12/8 MAIS MANIFEST dit 4/11/8` | +| **Fiche prose** `OTO Achat` `3/8/5→3/8/6` (table inchangée) | **exit 1** `fiche dit 3/8/6 MAIS MANIFEST 3/8/5` — mord là où la table seule est aveugle | +| **Cellule disparue** (`OTO Direction` retirée de la prose) | **exit 1** `claim INTROUVABLE (attendu 4/14/9)` | +| **Total** `5→6 Workspace natifs` | **exit 1** `fiche dit 6 MAIS MANIFEST dit 5` | +| Restauré | **exit 0** (10 lignes doc + total 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 ; aucun README/AGENT.md touché) | +| 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 chaque triplet depuis +`MANIFEST.workspaces` 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_073024` · Buffer S8 · Dernière surface de la classe « compte par suite » : les agrégats de tests rédigés EN PROSE dans deux fiches (`crm` « 81 (25+31+25) » · `erpnext_backend` « RBAC 60 (10+11+12+11+16) + e-CF 39 ») n'étaient couverts par AUCUN gate — une compensation entre suites laisserait la table gatée juste et la prose fausse **Tâche** : **Sprint 8 · buffer L75** (DevOps CI/CD · QA régression). Roadmap diff --git a/ci/README.md b/ci/README.md index 872cf46..456aa5b 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) == 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) == 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 @@ -154,6 +154,15 @@ Le **total ET le multiset des composants** (ordre-indépendant) sont recomputés table (gatée ci-dessus) juste et la prose fausse — c'est le même piège que les cellules, au niveau agrégé. +Même classe encore, hors du domaine « tests » : les comptes **par workspace** +(cartes / liens / rôles) des portails frontend, transcrits **à la main** dans +**deux** docs — la table du README du module (`frontend/portails/README.md`) **et** +la prose de la fiche agent (`03_agents/frontend_console/AGENT.md` : « `OTO Ventes` +4 cartes/11 liens/12 rôles · … »). Chaque triplet **et** le total « N `Workspace` +natifs » sont recomputés depuis `frontend/portails/out/MANIFEST.json` (tableau +`workspaces`, lui-même byte-gaté par `check_artifacts`) : ajouter un rôle/DocType +au contrat RBAC ferait dériver **les deux** docs en silence sinon. + 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 4d2706f..19c6ac7 100755 --- a/ci/check_readme_claims.sh +++ b/ci/check_readme_claims.sh @@ -42,6 +42,11 @@ # • « Total CRM N (a+b+c) » · « RBAC N (…) » · « e-CF N » — total ET multiset # des composants recomputés (une compensation entre suites laisserait la # table juste et la prose fausse) → plan.suites[path] +# frontend/portails/README.md + 03_agents/frontend_console/AGENT.md : +# • comptes PAR WORKSPACE (cartes / liens / rôles) transcrits À LA MAIN dans +# DEUX docs (table du README + prose « OTO Ventes 4 cartes/11 liens/12 +# rôles ») + total « N Workspace natifs » → portails MANIFEST +# (tableau `workspaces`, byte-gaté par check_artifacts) # 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é). # @@ -384,6 +389,78 @@ else: bad(f"Prose agrégée · e-CF — dit {mecf.group(1)} MAIS source dit {ecf_exp} " f"(fiscal/ecf_dgii) → régénérer la fiche") +# ============================================================================ +# Portails frontend · comptes PAR WORKSPACE (cartes / liens / rôles) — +# même classe de « vert trompeur », transcrits À LA MAIN dans DEUX docs. +# ---------------------------------------------------------------------------- +# Le générateur de portails émet out/MANIFEST.json dont le tableau `workspaces` +# porte, PAR portail, {nb_cartes, nb_liens, nb_roles} recalculés depuis le +# contrat RBAC à chaque build (byte-gaté par check_artifacts). Ces triplets sont +# recopiés À LA MAIN à DEUX endroits : la table du README du module +# (frontend/portails/README.md, colonnes Cartes|Liens|Rôles) ET la prose de la +# fiche agent (03_agents/frontend_console/AGENT.md — « OTO Ventes 4 cartes/11 +# liens/12 rôles · … »). Aucun gate ne les couvrait : ajouter un rôle/DocType au +# contrat ferait dériver LES DEUX docs en silence pendant que l'artefact dit +# autre chose — même piège que les cellules « Tests » et la prose agrégée. On +# RECOMPUTE ici chaque triplet depuis MANIFEST (déjà byte-gaté · zéro duplication +# du compteur du générateur) et on exige l'égalité avec les DEUX docs. Un +# workspace absent d'un doc échoue AUSSI (traçabilité). +# ============================================================================ +WS_RD = f"{D}/frontend/portails/README.md" +WS_FI = "03_agents/frontend_console/AGENT.md" +try: + ws_list = load("frontend/portails/out/MANIFEST.json")["workspaces"] +except (OSError, KeyError, ValueError) as e: + bad(f"Portails · MANIFEST illisible/inattendu : {e}"); ws_list = [] +try: + ws_rd = re.sub(r"\s+", " ", open(WS_RD, encoding="utf-8").read()) +except OSError as e: + bad(f"Portails · {WS_RD} illisible : {e}"); ws_rd = "" +try: + ws_fi = re.sub(r"\s+", " ", open(WS_FI, encoding="utf-8").read()) +except OSError as e: + bad(f"Portails · {WS_FI} illisible : {e}"); ws_fi = "" + +for w in ws_list: + portail, ws = w["portail"], w["workspace"] + exp = (str(w["nb_cartes"]), str(w["nb_liens"]), str(w["nb_roles"])) + src = f"cartes={exp[0]} liens={exp[1]} rôles={exp[2]}" + # 1) Table du README du module : | | `` | C | L | R | + rd_pat = (rf"\| {re.escape(portail)} \| `{re.escape(ws)}` \| " + r"(\d+) \| (\d+) \| (\d+) \|") + mrd = re.search(rd_pat, ws_rd) + if mrd is None: + bad(f"Portails README · {ws} — ligne INTROUVABLE dans {WS_RD} (attendu {src})") + elif mrd.groups() == exp: + good(f"Portails README · {ws} — {'/'.join(mrd.groups())} == MANIFEST ({src})") + else: + bad(f"Portails README · {ws} — {WS_RD} dit {'/'.join(mrd.groups())} MAIS " + f"MANIFEST dit {'/'.join(exp)} ({src}) → régénérer le README") + # 2) Prose de la fiche agent : `` C cartes/L liens/R rôles (ou C/L/R) + fi_pat = (rf"`{re.escape(ws)}` (\d+)(?: cartes)?/(\d+)(?: liens)?/" + r"(\d+)(?: rôles)?") + mfi = re.search(fi_pat, ws_fi) + if mfi is None: + bad(f"Portails fiche · {ws} — claim INTROUVABLE dans {WS_FI} (attendu {src})") + elif mfi.groups() == exp: + good(f"Portails fiche · {ws} — {'/'.join(mfi.groups())} == MANIFEST ({src})") + else: + bad(f"Portails fiche · {ws} — {WS_FI} dit {'/'.join(mfi.groups())} MAIS " + f"MANIFEST dit {'/'.join(exp)} ({src}) → régénérer la fiche") + +# Total de workspaces (« N `Workspace` natifs ») cohérent artefact ↔ fiche. +n_ws = len(ws_list) +if n_ws: + mtot = re.search(r"\*\*(\d+) `Workspace` natifs", ws_fi) + if mtot is None: + bad(f"Portails fiche · total — « N `Workspace` natifs » INTROUVABLE " + f"dans {WS_FI} (attendu {n_ws})") + elif int(mtot.group(1)) == n_ws: + good(f"Portails fiche · total — {mtot.group(1)} == MANIFEST ({n_ws} workspaces)") + else: + bad(f"Portails fiche · total — {WS_FI} dit {mtot.group(1)} MAIS " + f"MANIFEST dit {n_ws} → régénérer la fiche") + sys.exit(1 if FAIL else 0) PY rc=$?