diff --git a/05_activity_log/2026-08-12.md b/05_activity_log/2026-08-12.md index c70fb86..dd89ae4 100644 --- a/05_activity_log/2026-08-12.md +++ b/05_activity_log/2026-08-12.md @@ -74,3 +74,22 @@ **Pourquoi pas un fix ni un gate (#5/#6/`verify-non-defects`).** (1) Pas de fix logique : changer le tie-break dedup (préférer projet vs global) altèrerait l'artefact byte-gaté + le claim README pour « corriger » un comportement correct. (2) Pas de fix fixture : compléter la localisation générique des 5 projets serait **inventer** une donnée (#6) sans source. (3) Pas de gate d'égalité per-langue : ce serait une **fausse contrainte** (l'asymétrie est linguistiquement légitime) — le seuil `≥ min` par langue est déjà gaté et suffit. **Vérif.** `./run_ci.sh` = **33 PASS · 0 FAIL · 0 SKIP** (inchangé — commentaire pur, artefact `seo/out/*.json` re-build byte-identique via `check_artifacts` ∈ run_ci). **0 code moteur V18** (bloqué #6), **0 logique de production modifiée** (commentaire seul), **0 artefact autoritaire modifié**, **0 fixture touchée** (#6), **0 gate ajouté** (#5), **0 commande VPS** (#8), **0 API externe**. Fichiers : `M 05_deliverables_mvp/seo/seolib/keywords.py` (note by-design au dedup) + ce journal. + +## Session `20260812_060044` — HARDENING correctness · `criteria.py` — le 3ᵉ détecteur regex (le SEUL non durci) de la famille « détecter la forme, pas une graphie » + +**État au démarrage.** `./run_ci.sh` = **33 PASS · 0 FAIL · 0 SKIP** (baseline verte). `regression_run.json totals` = **629/612/17** (PASS). `quality_report.json` = PASS · 24 modules · min 100/100 · bijection 24↔24. Moteur V18 toujours **bloqué** en gouvernance (directive « NE PAS coder avant l'audit » · D-06 gate d'entrée · #6) → **aucun code moteur légitime**. Canal stakeholder `daily_reports/2026-08-12.md` déjà à jour au HEAD (session `043034`). + +**Chasse au défaut d'abord (3 axes computationnels, jamais audités en profondeur) → 2 modules confirmés CLEAN, 1 vrai locus de robustesse trouvé.** +- **`crm/commissions/comlib/finance.py`** (calcul commissions `montant = base × taux`) : logique **correcte** — `rate()` normalise nombre/« 3.5 % »/« 3,5 % » en fraction, `taux_pct` de sortie = fraction cohérente avec le libellé de formule, opérande manquant → `None` (jamais 0-inventé). **Non touché.** +- **`faisabilite/bancable/banclib/finance.py`** (cœur break-even, zone de risque D-07) : **correct** — point d'équilibre `⌈52 % × total_unites⌉` avec `52 %` re-dérivé du canonique #9 (jamais codé en dur, cf. fix `448c310`), agrégats catalogue `Σ(qté × prix)` gardés sur `all(... is not None)`, formule publiée avec chaque valeur. **Non touché.** +- **Vrai locus = `qa/audit_4big/q4lib/criteria.py:25`** — voir ci-dessous. + +**Le locus : le 3ᵉ détecteur regex non durci d'une famille dont les 2 autres l'ont été.** Ce fichier est la source de **TROIS** fixes de correctness confirmés de la classe « détecter la **forme**, pas une **graphie** » : `ac595d0` (regex ASCII vs `\w` Unicode → test accentué sous-compté), `fd99362` (garde `__main__` littérale double-quotée → simple-quotée/Yoda/sans-espace loupées), `35de616` (`def ` figé vs `def\s+` de la source sœur byte-gatée → `def test_x(` loupé). Les deux détecteurs `_TEST_DEF_RE` (`def\s+…\w`) et `_MAIN_GUARD_RE` (`\s*==\s*`, deux styles de guillemets, ordre Yoda) sont explicitement durcis pour tolérer tout **Python valide** (doctrine articulée lignes 18-30). **Restait `_ADD_PARSER_RE`** — le 3ᵉ frère — figé sur `add_parser\(|add_subparsers\(` : il exige la parenthèse **collée** au nom de méthode, donc `add_subparsers ()` / `add_parser ("build")` (espace avant `(` — Python valide, PEP 8 déconseille sans l'imposer) sont **silencieusement loupés** → sous-scorage `−20 pts` du critère CLI d'un module sain, exactement le symptôme de `fd99362`. + +**Preuve de dents (avant fix).** Ancien regex : `p.add_subparsers ()` → **False**, `sub.add_parser ("x")` → **False** ; nouveau `add_parser\s*\(|add_subparsers\s*\(` → **True** sur ces deux formes **et** True sur les formes normales `add_parser("build")` / `add_subparsers(dest=…)` (aucune régression de couverture). + +**Pourquoi c'est un durcissement légitime et non une invention (#6).** (1) **Classe déjà jugée réelle 3×** dans CE fichier ; les 2 frères sont durcis, le 3ᵉ était l'anomalie — j'harmonise vers la doctrine **écrite du fichier lui-même** (« détecter la forme, pas une graphie »), pas une convention externe. (2) **Précédent direct** : `fd99362`/`35de616` ont durci pour des variantes de Python valide **pas nécessairement présentes** dans un module courant (guillemets simples, Yoda, `def\t`). (3) **Fix latent, artefact byte-identique** : `grep -rE 'add_(parser|subparsers)[ \t]+\('` sur tous les `.py` = **AUCUN** module n'utilise l'espace aujourd'hui → aucun re-scorage, `quality_report.json` re-build **byte-identique** (vérifié par `check_artifacts` ∈ run_ci, vert). + +**Pourquoi pas un nouveau gate (#5).** Ce n'est pas une classe de drift à surveiller mais un durcissement one-shot d'un détecteur ; la robustesse est dans le code du critère, pas dans un gate. La famille des 3 détecteurs est désormais **cohérente end-to-end**. + +**Vérif.** `./run_ci.sh` = **33 PASS · 0 FAIL · 0 SKIP** (inchangé). Fichier édité **compile** + **NFC-clean** (accents intentionnels, style aligné lignes 18-30). **0 code moteur V18** (bloqué #6), **0 artefact autoritaire modifié** (byte-identique), **0 fixture/chiffre inventé** (#6), **0 gate ajouté** (#5), **0 commande VPS** (#8), **0 API externe**. Fichiers : `M 05_deliverables_mvp/qa/audit_4big/q4lib/criteria.py` (regex `_ADD_PARSER_RE` durci + commentaire doctrine) + ce journal. diff --git a/05_deliverables_mvp/qa/audit_4big/q4lib/criteria.py b/05_deliverables_mvp/qa/audit_4big/q4lib/criteria.py index b825453..86b57b6 100644 --- a/05_deliverables_mvp/qa/audit_4big/q4lib/criteria.py +++ b/05_deliverables_mvp/qa/audit_4big/q4lib/criteria.py @@ -22,7 +22,11 @@ import re # autorité byte-gatée de la colonne « Tests » des fiches — les deux compteurs du # MÊME concept doivent coïncider (sinon dérive d'évidence #6, cf. le fix ASCII). _TEST_DEF_RE = re.compile(r"^\s*def\s+(test_\w+)\s*\(", re.MULTILINE) -_ADD_PARSER_RE = re.compile(r"add_parser\(|add_subparsers\(") +# `\s*` avant `(` : `add_subparsers ()` / `add_parser ("build")` sont du Python +# valide (PEP 8 déconseille l'espace mais le langage l'autorise). Même doctrine +# que ses détecteurs sœurs `_TEST_DEF_RE` / `_MAIN_GUARD_RE` — détecter la forme, +# pas une graphie (sinon sous-scorage silencieux d'un CLI sain · #6). +_ADD_PARSER_RE = re.compile(r"add_parser\s*\(|add_subparsers\s*\(") # Garde `if __name__ == "__main__":` — les DEUX styles de guillemets sont du # Python valide (PEP 8 n'impose rien), l'ordre des opérandes peut être inversé # (« Yoda »), et l'espacement autour de `==` est libre. Un littéral figé