diff --git a/05_activity_log/2026-08-05.md b/05_activity_log/2026-08-05.md index 0e77f36..819bd7f 100644 --- a/05_activity_log/2026-08-05.md +++ b/05_activity_log/2026-08-05.md @@ -1520,3 +1520,47 @@ la main** (#6 — les 20/30 sont lus du spec, non retapés), **aucune commande V journal. **Vérif.** `./run_ci.sh` → **33 PASS · 0 FAIL · 0 SKIP** (aucun fichier gaté touché). + +--- + +## Session 20260805_174324 · GATE NEUF — prose #9 des modules bancable + commissions ancrée sur CLAUDE.md + +**Contexte.** Repo vert au démarrage (33 PASS · 0 FAIL · 0 SKIP), tous modules livrés, +roadmap 8-sem sans tâche-feature ouverte. Sonde ciblée sur la classe **claude-md-constant-anchor-gate** : +les constantes canoniques #9 (3 % édition · 8.5 % marketing · 52 % point d'équilibre) +recopiées hors du module generator. + +**Constat (mutation-prouvé).** `sed` #9 `8.5 %→9 %` sur `CLAUDE.md` → le gate +`check_readme_claims.sh` fait rougir **8 surfaces generator/template** (model.py CANONICAL, +README, renderer `_CANONICAL_MARKERS`, oracle test, 2 docstrings scorer, template intro/§5.2/§7) +— l'ancre est vivante. **MAIS 4 copies restaient périmées EN SILENCE**, dans DEUX modules +DISTINCTS, chacune se réclamant explicitement « (#9) » / « CLAUDE.md #9/#10 » : +- (a) `faisabilite/bancable/README.md` — « 3 % édition » et « 8.5 % marketing » (#9) +- (b) `faisabilite/bancable/banclib/finance.py` — MÊME phrase, en docstring +- (c) `faisabilite/bancable/banclib/deps.py` — « CLAUDE.md #9/#10 (3 %/8.5 %/52 % · USD+DOP · Cardnet · Letter US) » (6 marqueurs) +- (d) `crm/commissions/README.md` — « pourcentages canoniques (3 % édition · 8.5 % marketing · 52 % point d'équilibre) » + +Corriger le generator ne TOUCHE pas ces modules → invention #6 latente, de la classe déjà +fermée 10× (publiciste #10 · fiscal moneda · fiches #10 · template canonique). + +**Fix.** Extension du bloc d'ancrage existant dans `ci/check_readme_claims.sh` (juste après +« Template canonique »), **réutilisant `exp`** déjà recomputé des lignes #9/#10 de `CLAUDE.md` +(zéro re-parse, zéro duplication). Un claim absent échoue AUSSI (traçabilité #6). Documenté +en `ci/README.md` §2 (paragraphe « paramètres canoniques »), pas de nouvelle ligne au +tableau §1 (cf. [[ci-readme-table-detail-in-section2]]). + +**Teeth (mutation-vérifié, arbre restauré à chaque fois).** +- `#9 8.5 %→9 %` → mord les **4** surfaces. +- `#9 52 %→55 %` → mord **(c)+(d) SEULEMENT** ; (a)/(b) ne citent qu'édition+marketing ⇒ + correctement épargnées = **ciblage précis** (pas de faux-rouge). +- `#10 Cardnet→Azul` → mord l'énumération 6-marqueurs de (c). +- Arbre restauré → gate exit=0, les 4 surfaces re-vertes. + +**Portée / anti-churn.** **0 fichier de production édité** (seuls `ci/check_readme_claims.sh` ++ `ci/README.md`), **0 artefact reconstruit** (aucun `out/*.json` ni module doc scoré par +audit_4big → pas de rebuild `quality_report`), **0 chiffre saisi à la main** (#6 — les 6 +valeurs sont recomputées de `CLAUDE.md`, jamais retapées), **aucune commande VPS** (#8 — +100 % local). Classe RÉCURRENTE (chaque module distinct qui recopie une constante #9/#10 +citée est une surface à ancrer) : nouvelle sous-surface (bancable + commissions) close. + +**Vérif.** `./run_ci.sh` → **33 PASS · 0 FAIL · 0 SKIP**. diff --git a/ci/README.md b/ci/README.md index 0a42505..0f575d8 100644 --- a/ci/README.md +++ b/ci/README.md @@ -1196,6 +1196,18 @@ vérifiées : renderer `Cardnet→Azul` (absent/en-trop) · test oracle retire ` mord SIMULTANÉMENT model.py + renderer + test + les DEUX docstrings** (la valeur même de l'ancrage) ; restauré = vert. +Même classe enfin sur QUATRE copies **hors du module generator** — la prose #9 de +DEUX modules distincts qui se réclame explicitement « (#9) » / « CLAUDE.md #9/#10 » +mais qu'aucun gate ne liait à la contrainte (corriger le generator ne les touche pas) : +(a) `faisabilite/bancable/README.md` et (b) sa docstring `banclib/finance.py` +(« 3 % édition » et « 8.5 % marketing » (#9)), (c) `banclib/deps.py` +(« CLAUDE.md #9/#10 (3 %/8.5 %/52 % · USD+DOP · Cardnet · Letter US) », 6 marqueurs), +(d) `crm/commissions/README.md` (« pourcentages canoniques (3 % édition · 8.5 % +marketing · 52 % point d'équilibre) »). On RÉUTILISE `exp` (zéro re-parse). Morsures +vérifiées : `#9 8.5 %→9 %` mord les 4 · `#9 52 %→55 %` mord (c)+(d) SEULS ((a)/(b) ne +citent qu'édition+marketing ⇒ correctement épargnées, ciblage précis) · `#10 +Cardnet→Azul` mord l'énumération (c). Un claim absent échoue AUSSI (traçabilité #6). + Même classe enfin sur la **fiche d'identité du rôle RBAC mobile** — la fiche agent `03_agents/mobile/AGENT.md`. Les builds/soumissions stores étant **hors-repo** (#8), l'agent mobile le reconnaît et déclare que son **seul ancrage in-repo vérifiable** est diff --git a/ci/check_readme_claims.sh b/ci/check_readme_claims.sh index 445e4de..a696c9a 100755 --- a/ci/check_readme_claims.sh +++ b/ci/check_readme_claims.sh @@ -5864,6 +5864,99 @@ if exp is not None: + (f" · absents={miss}" if miss else "") + (f" · en trop={extra}" if extra else "") + " → régénérer le template") +# ============================================================================ +# Bancable + Commissions · PROSE #9 (« pourcentages canoniques ») ANCRÉE sur +# CLAUDE.md — même classe d'ANCRAGE que « paramètres canoniques » (generator) et +# « Template canonique » ci-dessus, mais sur des MODULES DISTINCTS +# (faisabilite/bancable, crm/commissions) dont la doc RECOPIE à la main les +# pourcentages #9 en se réclamant EXPLICITEMENT « (#9) » / « CLAUDE.md #9/#10 », +# sans qu'aucun gate ne les liât à la contrainte. +# ---------------------------------------------------------------------------- +# Le bloc « paramètres canoniques » ancre le SEUL module generator (model.py / +# README / renderer / scorer / test) + le TEMPLATE. Restent QUATRE copies +# data-derived dans DEUX autres modules, toutes invisibles à check_artifacts +# (prose/docstrings hors out/) et non attrapées par leurs tests/ (FONCTIONS) : +# (a) faisabilite/bancable/README.md — « 3 % édition » et « 8.5 % marketing » (#9) +# (b) faisabilite/bancable/banclib/finance.py — MÊME phrase, en docstring +# (c) faisabilite/bancable/banclib/deps.py — « CLAUDE.md #9/#10 (3 %/8.5 %/52 %·…) » +# (d) crm/commissions/README.md — « (3 % édition · 8.5 % marketing · 52 % …) » +# La mutation #9 (ex. 8.5 %→9 %) fait DÉJÀ rougir les 8 surfaces generator/template, +# MAIS ces 4 copies-là restent périmées EN SILENCE : corriger le generator ne les +# TOUCHE pas (modules distincts) — invention #6 latente, de la classe déjà fermée +# 10× pour publiciste #10 / fiscal moneda / fiches #10. On RÉUTILISE `exp` +# (recompute #9 depuis CLAUDE.md · zéro duplication). Un claim absent échoue AUSSI. +# ============================================================================ +if exp is not None: + _p_edt = _pnorm(exp["frais_edition_pct"]); _p_mkt = _pnorm(exp["marketing_pct"]) + _p_eql = _pnorm(exp["point_equilibre_pct"]) + _want6b = {_pnorm(exp[k]) for k in _num_keys} | {_ai(exp[k]) for k in _txt_keys} + # (a)+(b) même phrase « « E % édition » et « M % marketing » (#9) » + _duo_re = re.compile(r"«\s*([\d.]+)\s*%\s*[ée]dition\s*»\s*et\s*" + r"«\s*([\d.]+)\s*%\s*marketing\s*»\s*\(#9\)") + for _lbl, _rel in (("bancable/README", "faisabilite/bancable/README.md"), + ("bancable/finance.py", "faisabilite/bancable/banclib/finance.py")): + _txt = read_norm(f"{D}/{_rel}") + if _txt is None: + bad(f"Canoniques prose #9 · {_lbl} illisible"); continue + _mm = _duo_re.search(_txt) + if _mm is None: + bad(f"Canoniques prose #9 · {_lbl} — phrase « « N % édition » et " + f"« N % marketing » (#9) » INTROUVABLE (traçabilité #6)") + else: + _got = {_pnorm(float(_mm.group(1))), _pnorm(float(_mm.group(2)))} + _want = {_p_edt, _p_mkt} + if _got == _want: + good(f"Canoniques prose #9 · {_lbl} — {_p_edt} édition · {_p_mkt} " + f"marketing == CLAUDE.md #9") + else: + bad(f"Canoniques prose #9 · {_lbl} — cite {sorted(_got)} MAIS CLAUDE.md " + f"{sorted(_want)} → aligner (ou CLAUDE.md)") + # (c) deps.py « CLAUDE.md #9/#10 (3 % / 8.5 % / 52 % · USD+DOP · Cardnet · Letter US) » + _dep = read_norm(f"{D}/faisabilite/bancable/banclib/deps.py") + if _dep is None: + bad("Canoniques prose #9 · bancable/deps.py illisible") + else: + _md = re.search(r"CLAUDE\.md #9/#10\s*\(([^)]+)\)", _dep) + if _md is None: + bad("Canoniques prose #9 · bancable/deps.py — « CLAUDE.md #9/#10 (…) » " + "INTROUVABLE (traçabilité #6)") + else: + _got = set() + for _tok in re.split(r"[/·]", _md.group(1)): + _p = _pct(_tok) + _got.add(_pnorm(_p) if _p is not None else _ai(_tok)) + _got.discard("") + if _got == _want6b: + good("Canoniques prose #9 · bancable/deps.py — 6 marqueurs == " + "CLAUDE.md #9/#10") + else: + _miss = sorted(_want6b - _got); _extra = sorted(_got - _want6b) + bad(f"Canoniques prose #9 · bancable/deps.py — énumère {sorted(_got)} MAIS " + f"CLAUDE.md {sorted(_want6b)}" + + (f" · absents={_miss}" if _miss else "") + + (f" · en trop={_extra}" if _extra else "") + " → aligner (ou CLAUDE.md)") + # (d) commissions/README « pourcentages canoniques (E % édition · M % marketing · Q % …) » + _com = read_norm(f"{D}/crm/commissions/README.md") + if _com is None: + bad("Canoniques prose #9 · commissions/README illisible") + else: + _mc = re.search(r"pourcentages canoniques\s*\(([^)]*%[^)]*)\)", _com) + if _mc is None: + bad("Canoniques prose #9 · commissions/README — « pourcentages canoniques " + "(… %) » INTROUVABLE (traçabilité #6)") + else: + _got = {_pnorm(float(_x)) for _x in re.findall(r"([\d.]+)\s*%", _mc.group(1))} + _want = {_p_edt, _p_mkt, _p_eql} + if _got == _want: + good(f"Canoniques prose #9 · commissions/README — {sorted(_want)} == " + f"CLAUDE.md #9") + else: + _miss = sorted(_want - _got); _extra = sorted(_got - _want) + bad(f"Canoniques prose #9 · commissions/README — cite {sorted(_got)} MAIS " + f"CLAUDE.md {sorted(_want)}" + + (f" · absents={_miss}" if _miss else "") + + (f" · en trop={_extra}" if _extra else "") + " → aligner (ou CLAUDE.md)") + # ============================================================================ # Fiscal · ecf_dgii — le JEU DE DEVISES `moneda` (`USD`/`DOP`) ANCRÉ sur CLAUDE.md # #10 — même classe d'ANCRAGE que la CITATION #10 des fiches ci-dessus, mais sur