From cb8069b83c6a856c48909840a8250869dc2c57dc Mon Sep 17 00:00:00 2001 From: Claude Code DTP Worker Date: Wed, 12 Aug 2026 09:11:53 +0000 Subject: [PATCH] =?UTF-8?q?[DTP-Worker=2020260812=5F090114]=20d=C3=A9faut?= =?UTF-8?q?=20d'abord=20=C2=B7=20chasse=20DRY=20sur=208=20modules=20non-au?= =?UTF-8?q?dit=C3=A9s=20=C2=B7=20durcissement=20anti-r=C3=A9currence=20des?= =?UTF-8?q?=203=20sites=20:g-domaine-born=C3=A9=20(commentaires=20purs=20?= =?UTF-8?q?=C2=B7=200=20impact=20artefact)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Explore very-thorough sur crm/dossier_vente · crm/financement_bancaire · faisabilite/bancable · frontend/chat_otoia · pie/manifest · qa/audit_4big · mobile/app_config · fiscal/ecf_dgii → 0 défaut réel. Les 4 candidats remontés sont tous NON-défauts (verify-non-defects #6) : 3× `:g` sur domaine BORNÉ (nombre d'unités / pourcentages 0-100 → exponentiel + arrondi-6-chiffres hors domaine, et `:g` nettoie le bruit flottant) + 1× HTML chat_otoia à entrée canonique contrôlée. Preuve robuste : report.py `_money` réserve sciemment `,.0f`/`,.2f` aux montants non bornés → les `:g` restants sont un choix d'ingénierie correct, pas un oubli. Fait nouveau : le taux commissions `:g` (déjà statué non-défaut en 080104) a RÉCIDIVÉ de flag ce cycle, et bancable ×2 fraîchement flaggés → la classe gaspille un cycle d'audit à chaque passage. `grep ':g}'` prod = exactement 3 sites. Passe unique : note de justification in-situ à chacun (domaine borné, 2 pièges hors domaine, montants → `,.0f`, « ne pas corriger »). Convertit le faux-positif récurrent en « déjà revu, sûr ». 0 fix de code (défauts inexistants ; corriger `:g`→`:f` réintroduirait du bruit flottant = régression) · 0 gate (#5) · 0 artefact reconstruit (bancable sans out/ ; _rate_label runtime non sérialisé) · matrice inchangée 633/616/17 · run_ci 33/0/0 · NFC-clean · 0 code moteur V18 (bloqué D-06) · 0 commande VPS. M crm/commissions/commlib/finance.py M faisabilite/bancable/banclib/finance.py M faisabilite/bancable/banclib/report.py M 05_activity_log/2026-08-12.md Co-Authored-By: Claude Opus 4.8 (1M context) --- 05_activity_log/2026-08-12.md | 20 +++++++++++++++++++ .../crm/commissions/commlib/finance.py | 10 ++++++++-- .../faisabilite/bancable/banclib/finance.py | 5 +++++ .../faisabilite/bancable/banclib/report.py | 4 ++++ 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/05_activity_log/2026-08-12.md b/05_activity_log/2026-08-12.md index 2385132..d269e9b 100644 --- a/05_activity_log/2026-08-12.md +++ b/05_activity_log/2026-08-12.md @@ -195,3 +195,23 @@ Chaque incrément correspond à un **test de régression à dents** ajouté par **Pourquoi pas un fix ni un gate (#5/#6).** Aucun défaut trouvé (Explore CLEAN + tree cohérent) ; le seul « écart » était la **staleness du canal stakeholder** (canal éditorial daté par design, comme un snapshot — pas un artefact byte-gaté). L'actualiser EST le contrat de `two-logging-channels`, pas une matière à gate. Le batch des 3 fixes en une seule réconciliation était l'intention explicite des sessions `073055`/`080104`. **Vérif.** `./run_ci.sh` = **33 PASS · 0 FAIL · 0 SKIP** (inchangé — rapport = document, 0 dépendance de gate). Rapport **NFC-clean** (vérifié). **0 code moteur V18** (bloqué #6), **0 module de production touché**, **0 artefact autoritaire modifié/reconstruit**, **0 chiffre figé** (tous lus depuis les artefacts commités · #6), **0 gate ajouté** (#5), **0 commande VPS** (#8), **0 API externe**. Fichiers : `M 05_deliverables_mvp/daily_reports/2026-08-12.md` (réconciliation au HEAD courant) + ce journal. + +## Session `20260812_090114` — DÉFAUT D'ABORD : chasse DRY sur 8 modules jamais ré-audités ce cycle · durcissement anti-récurrence des 3 sites `:g`-domaine-borné (commentaires purs · 0 impact) + +**État au démarrage.** `./run_ci.sh` = **33 PASS · 0 FAIL · 0 SKIP** (baseline verte). `regression_run.json` (lu depuis l'artefact commité · #6) = **633 exécutés · 616 passés · 17 skippés · 0 échec** (PASS). `quality_report.json` = **PASS · 24 modules · min 100/100 · bijection 24↔24**. Séquence moteur V18 toujours **bloquée** en gouvernance (D-06 = gate d'entrée · « NE PAS coder avant l'audit » · #6) → **aucun code moteur légitime**. Arbitrages produit D-01→D-08 en attente Michel (`OPEN_DECISIONS_REGISTER.md`). Canal stakeholder `daily_reports/2026-08-12.md` réconcilié à 633 par la session `083114` → à jour. + +**Chasse au défaut (Explore very-thorough) sur les 8 modules JAMAIS ré-audités ce cycle → DRY (0 défaut réel).** Ciblé : `crm/dossier_vente` · `crm/financement_bancaire` · `faisabilite/bancable` · `frontend/chat_otoia` · `pie/manifest` · `qa/audit_4big` · `mobile/app_config` · `fiscal/ecf_dgii`. L'Explore a remonté 4 « candidats » (2 HAUTE, 2 MOYENNE) — **tous vérifiés NON-défauts** : +- **BUG #1** `bancable/banclib/finance.py:138` `f"{pe_brut:g}"` — `pe_brut = 0.52 × NOMBRE d'unités`, domaine **borné** (quelques centaines en immobilier réel). L'exponentiel (≥ 1e6) exige ~1.9M unités : irréaliste. L'arrondi 6-chiffres idem hors domaine. En prime `:g` nettoie le bruit flottant du produit (`0.52 × 173 = 89.96000…1 → « 89.96 »`). Scénario Explore « 2 000 000 unités » = fabriqué. +- **BUG #2** `bancable/banclib/report.py:38` `_pct` `f"{n:g} %"` — pourcentage (marge/taux) **borné 0-100** : mêmes deux pièges hors domaine. +- **BUG #3** `chat_otoia/chatlib/frappe.py:43-44` HTML non échappé — `mount_html` ne reçoit QUE des slugs canoniques contrôlés (préfixe/classe de config, clé portail, persona sourcée CLAUDE.md) ; aucune donnée libre → injection inatteignable. Fragilité défensive-seule (`verify-non-defects` · #6). +- **BUG #4** `commissions/commlib/finance.py:75` taux `:g` — **déjà statué non-défaut** par la session `080104` (« taux borné 0-100 ») ; **RÉCIDIVE** de flag ce cycle. + +**Preuve de non-défaut robuste (pas seulement « scénario irréaliste »).** `report.py:_money` (l.25) utilise **sciemment** `,.0f`/`,.2f` (jamais `:g`) pour les montants **non bornés** (USD 18 000 000 → « 18,000,000 »). Les auteurs ont donc **délibérément** réservé `:g` aux domaines bornés (unités, pourcentages) et `,.0f` aux montants → les 3 `:g` restants sont un **choix d'ingénierie correct**, pas un oubli. C'est la distinction exacte de la mémoire `docstring-vs-code-drift`/`verify-non-defects` : « le contexte de sortie dicte l'encodeur, ET le domaine borne le risque ». + +**Action = durcissement anti-récurrence (commentaires purs · sœur de « twin fix must sweep ALL surfaces in one pass »).** Fait nouveau : BUG #4 a **déjà récidivé** malgré la décision « laissé tel quel » de `080104` (aucune note in-situ), et BUG #1/#2 sont fraîchement flaggés HAUTE — la classe `:g`-domaine-borné **gaspille un cycle d'audit à chaque passage**. `grep ':g}'` sur tout le code de prod = **exactement 3 sites** (les 3 flaggés). Passe unique : ajout à chacun d'une **note de justification in-situ** (domaine borné → 2 pièges de `:g` hors domaine + nettoyage du bruit flottant + « montants → `,.0f`/`_amount_label`, jamais `:g` » + « ne pas corriger »). Convertit le faux-positif récurrent en « déjà revu, sûr » pour les audits futurs. + +**Pourquoi PAS un fix de code ni un gate (#5/#6).** Aucun défaut réel : « corriger » `:g`→`:f` **casserait** du code correct (ré-introduirait du bruit flottant `« 89.96 »→« 89.960000 »`) — une régression, exactement l'inverse d'un fix. Robustesse déjà portée par le domaine borné + le choix `,.0f` sur les montants ; rien de nouveau à gater (#5). Commentaires sans marqueur `# N ·` d'invariant (pas de déclenchement du gate ledger). + +**Impact d'artefact : ZÉRO.** Commentaires purs → aucun changement de sortie. `bancable` n'a pas d'`out/` commité (`bancable-out-not-committed`). `_rate_label` est du code **runtime** (`compute_line`), jamais appelé par `commissions_gen.py`, et aucune `formule`/`base` n'est sérialisée dans les artefacts commités. `regression_run.json`/`quality_report.json` **inchangés** (633/616/17 · 24×100). Aucun compteur de fiche/README à réaligner. + +**Vérif.** `./run_ci.sh` = **33 PASS · 0 FAIL · 0 SKIP** (inchangé). 3 fichiers édités **compilent** + **NFC-clean** (vérifié). **0 code moteur V18** (bloqué #6), **0 fix de code** (défauts inexistants), **0 chiffre/fixture inventé** (#6), **0 gate ajouté** (#5), **0 artefact modifié/reconstruit**, **0 commande VPS** (#8), **0 API externe**. Fichiers : `M crm/commissions/commlib/finance.py` · `M faisabilite/bancable/banclib/finance.py` · `M faisabilite/bancable/banclib/report.py` (notes anti-récurrence `:g`) + ce journal. diff --git a/05_deliverables_mvp/crm/commissions/commlib/finance.py b/05_deliverables_mvp/crm/commissions/commlib/finance.py index 5658c45..b766793 100644 --- a/05_deliverables_mvp/crm/commissions/commlib/finance.py +++ b/05_deliverables_mvp/crm/commissions/commlib/finance.py @@ -68,11 +68,17 @@ def _amount_label(x: float) -> str: def _rate_label(value: Any) -> str: - """Libellé du taux tel qu'affiché dans la formule (verbatim si texte).""" + """Libellé du taux tel qu'affiché dans la formule (verbatim si texte). + + NB : `:g` est VOLONTAIRE et sûr ici (≠ montants → `_amount_label`). Un taux de + commission est borné (0-100), donc jamais en notation exponentielle (< 1e6) et + jamais au-delà de 6 chiffres significatifs : les deux pièges de `:g` documentés + sur `_amount_label` sont inatteignables sur ce domaine, et `:g` nettoie en prime + le bruit flottant (« 3.0 » → « 3 »). Ne pas « corriger » en `:f` (audit récurrent).""" if not is_filled(value): return "{taux_pct}" if isinstance(value, (int, float)) and not isinstance(value, bool): - return f"{value:g} %" + return f"{value:g} %" # borné 0-100 : sûr, cf. docstring return str(value).strip() diff --git a/05_deliverables_mvp/faisabilite/bancable/banclib/finance.py b/05_deliverables_mvp/faisabilite/bancable/banclib/finance.py index 3b97312..d266d15 100644 --- a/05_deliverables_mvp/faisabilite/bancable/banclib/finance.py +++ b/05_deliverables_mvp/faisabilite/bancable/banclib/finance.py @@ -134,6 +134,11 @@ def derived(brief: dict) -> list[dict]: if total_unites is not None and pe_pct is not None: pe_brut = pe_pct * total_unites pe_unites = float(math.ceil(pe_brut)) + # `:g` sûr ici : pe_brut = 0.52 × NOMBRE d'unités (domaine borné à quelques + # centaines en immobilier réel) — jamais ≥ 1e6 (pas d'exponentiel) ni > 6 + # chiffres significatifs, les deux pièges de `:g`. En prime `:g` nettoie le + # bruit flottant du produit (0.52 × 173 = 89.96000…1 → « 89.96 »). Les + # MONTANTS non bornés, eux, passent par _money (`,.0f`), jamais par `:g`. pe_formule = (f"{CANONICAL['point_equilibre_pct']} × {int(total_unites)} = " f"{pe_brut:g}") else: diff --git a/05_deliverables_mvp/faisabilite/bancable/banclib/report.py b/05_deliverables_mvp/faisabilite/bancable/banclib/report.py index 9b42e0d..4a3c90f 100644 --- a/05_deliverables_mvp/faisabilite/bancable/banclib/report.py +++ b/05_deliverables_mvp/faisabilite/bancable/banclib/report.py @@ -33,6 +33,10 @@ def _int(n: Optional[float], placeholder: str) -> str: def _pct(n: Optional[float], placeholder: str) -> str: + # `:g` sûr : un pourcentage (marge, taux) est borné (0-100), donc jamais en + # exponentiel (< 1e6) ni > 6 chiffres significatifs — les deux pièges de `:g` + # sont hors domaine ; `:g` nettoie en prime le bruit flottant. Les MONTANTS + # passent par _money (`,.0f`/`,.2f`), jamais par `:g`. Ne pas « corriger ». if n is None: return "{{" + placeholder + "}}" return f"{n:g} %"