diff --git a/03_agents/erpnext_backend/AGENT.md b/03_agents/erpnext_backend/AGENT.md index 0d6f900..e42164e 100644 --- a/03_agents/erpnext_backend/AGENT.md +++ b/03_agents/erpnext_backend/AGENT.md @@ -37,7 +37,7 @@ le VPS reçoit — jamais l'inverse. vente, le barème commissions, le DocType Dossier Vente et le plan e-CF **résolvent** leurs rôles depuis ce fichier, jamais un nom Frappe en dur. Total backend **RBAC 60 tests** (10 + 11 + 12 + 11 + 16) + **e-CF 39 tests**, tous gated dans le CI (matrice -de régression du repo : **627 tests · 24 suites** · verdict `PASS`, source +de régression du repo : **628 tests · 24 suites** · verdict `PASS`, source `qa/regression/out/regression_run.json` — jamais compté à la main · #6). ## Hand-off reçus (à appliquer sur le VPS, dans l'ordre) diff --git a/03_agents/qa/AGENT.md b/03_agents/qa/AGENT.md index fb043b4..4d84155 100644 --- a/03_agents/qa/AGENT.md +++ b/03_agents/qa/AGENT.md @@ -22,7 +22,7 @@ périmètre — un auditeur ne s'audite jamais. | Module | Sprint | Rôle | Entrée CLI | Job CI | Tests | |---|---|---|---|---|---| | [`audit_5d/`](../../05_deliverables_mvp/qa/audit_5d/README.md) | 5 (roadmap L57) | Audit **5D** de conformité : 17 contrôles / 5 dimensions, lit les `out/` amont, coche UAF + normes ISA/IFRS | `audit_5d_gen.py build\|validate` | `qa-audit-5d-tests` | 37 | -| [`audit_4big/`](../../05_deliverables_mvp/qa/audit_4big/README.md) | 7 (roadmap L69) | **Gate qualité** : note 4Big de 100 % des livrables, verdict `FAIL` si un module < 95/100 ou couverture incomplète | `audit_4big_gen.py build\|validate` | `qa-audit-4big-tests` | 36 | +| [`audit_4big/`](../../05_deliverables_mvp/qa/audit_4big/README.md) | 7 (roadmap L69) | **Gate qualité** : note 4Big de 100 % des livrables, verdict `FAIL` si un module < 95/100 ou couverture incomplète | `audit_4big_gen.py build\|validate` | `qa-audit-4big-tests` | 37 | | [`regression/`](../../05_deliverables_mvp/qa/regression/README.md) | 8 (roadmap L74) | **Matrice de régression** : agrège l'exécution de **toutes** les suites gated en un verdict unique + le compte agrégé faisant autorité | `regression_gen.py build\|validate\|run` | `qa-regression-tests` | 26 | | [`acceptance/`](../../05_deliverables_mvp/qa/acceptance/README.md) | 8 (roadmap L75 / L80-87) | **Recette / traçabilité** : mappe chaque promesse roadmap (8 livrables + 7 métriques) vers sa preuve, bijectif vs CI | `acceptance_gen.py build\|validate` | `qa-acceptance-tests` | 37 | @@ -32,7 +32,7 @@ périmètre — un auditeur ne s'audite jamais. **conformité** normative des hand-off. ## Verdict agrégé courant (source `qa/regression/out/regression_run.json`) -**24 suites gated · 627 tests · 610 passés · 0 échec · 0 erreur → verdict `PASS`.** +**24 suites gated · 628 tests · 611 passés · 0 échec · 0 erreur → verdict `PASS`.** Ce compte est **découvert automatiquement depuis le CI** (`q4lib.registry.parse_ci`, réutilisé sans duplication) — jamais compté à la main. Les **17 tests restants sont ignorés** (`skipped`) : ce sont les *oracles optionnels* diff --git a/05_activity_log/2026-08-11.md b/05_activity_log/2026-08-11.md index b615e3e..7ec9681 100644 --- a/05_activity_log/2026-08-11.md +++ b/05_activity_log/2026-08-11.md @@ -239,3 +239,27 @@ Un lecteur (agent ou humain) du README — le doc le plus consulté — pouvait **Pourquoi pas un nouveau gate (#5).** La divergence de compte est déjà **structurellement gatée** (`check_readme_claims` a immédiatement rougi la fiche QA + le compte `audit_4big`) et la teeth verrouille le comportement de `crit_cli`. Occurrence latente (0 garde simple-quotée aujourd'hui) mais correction de contrat de l'outil, pas un cas d'entrée produit → pas de gate additionnel. **Vérif.** `./run_ci.sh` = **33 PASS · 0 FAIL · 0 SKIP** rétabli après régénération. **0 code moteur V18** (bloqué #6), **0 commande VPS** (#8), **0 API externe**. Fichiers : `qa/audit_4big/q4lib/criteria.py` (regex) + `qa/audit_4big/tests/test_audit_4big.py` (teeth) + `qa/regression/out/{regression_plan,regression_run,MANIFEST}.json` + `03_agents/qa/AGENT.md` + `03_agents/erpnext_backend/AGENT.md` + ce journal. `quality_report.json` **non touché** (auto-exclusion prouvée). + +--- + +## Session `20260811_085834` — HARDENING correctness · l'alignement du compteur de tests `audit_4big`↔`discovery` (fix ASCII `ac595d0`) était INCOMPLET : `def ` (un espace figé) ≠ `def\s+` de la sœur — teeth ajoutée, classe désormais close + +**État au démarrage.** `./run_ci.sh` = **33 PASS · 0 FAIL · 0 SKIP** (baseline verte). Séquence moteur V18 toujours **intégralement bloquée** en gouvernance (D-06 approbation de l'audit = gate d'entrée · D-07 formules DCF/IRR/VAN/DSCR/LTV/LTC absentes · D-08 Master Data Model sur-ensemble strict · directive `:90-91` « NE PAS coder avant l'audit » · #6) — **aucun code moteur légitime**. Les bannières V18 des 4 modules-origine, le liage index et les 2 fixes `audit_4big` (regex ASCII `ac595d0` · garde `__main__` `fd99362`) sont clos. Les 2 directives 08-10 (`OTO_3D_STUDIO`, `COMPTE_CLIENT_COURRIELS`) restent hors périmètre repo. + +**Défaut trouvé (classe #6 · MÊME racine que les 2 fixes précédents : « détecter la forme, pas une graphie » · + il COMPLÈTE un fix laissé à moitié).** Le fix ASCII `ac595d0` (session `072823`) avait pour but explicite d'**aligner** le compteur de méthodes de test d'`audit_4big` (`q4lib/criteria.py:_TEST_DEF_RE`) sur celui de la source sœur `reglib/discovery.py:_TEST_METHOD_RE` — autorité **byte-gatée** de la colonne « Tests » des fiches (réutilisée par `check_readme_claims` via `discovery.count_tests`). Il a aligné le **char-class** (`[A-Za-z0-9_]` → `\w`, pour les noms accentués), mais a laissé **une seconde divergence intacte** sur le **séparateur `def`** : +- `discovery.py:23` = `r"^\s*def\s+(test_\w+)\s*\("` — `\s+` (espacement libre) +- `criteria.py:18` = `r"^\s*def (test_\w+)\s*\("` — **un seul espace littéral figé** + +Or `def test_x(` (deux espaces) et `def\ttest_x(` (tabulation) sont du **Python valide** (PEP 8 recommande un espace, le langage n'en impose pas). Le littéral `def ` les **loupe** → `crit_tests` sous-compterait, en **désaccord d'un ou plusieurs** avec la sœur — exactement le type de dérive d'évidence #6 que le fix ASCII prétendait clore. Les **deux compteurs du MÊME concept** doivent coïncider sur **toute graphie valide**, pas seulement les identifiants Unicode. + +**Occurrence (comme la garde `__main__` de `fd99362`) : latente, 0 aujourd'hui.** `grep -rnE '^\s*def( +|\t)test_'` sur tous les `test_*.py` = **vide** (tous en un-espace PEP 8) → aucun mis-comptage courant, `quality_report.json` **byte-identique**. Mais c'est la **correction du contrat de détection de l'outil d'enforcement #6** (compter correctement n'importe quel Python valide + coïncider avec la sœur), pas une sur-défense d'un cas d'entrée produit improbable — même base d'acceptation que `fd99362`. Divergence sœur de recensement de fichiers vérifiée aussi : `criteria` recurse (`os.walk`) là où `discovery` liste à plat (`os.listdir`) — **aucun `tests/*/test_*.py` imbriqué dans le dépôt** (`find` vide) → non-occurrent, axe distinct (découverte de fichiers, pas détection de méthode), **laissé** (discovery = autorité du gate ; ne pas muter son comportement pour un cas nul). + +**Fix (chirurgical · forme, pas graphie · single-source-de-forme · teeth).** `criteria.py:18` : `def ` → `def\s+`, forme **strictement identique** à `discovery._TEST_METHOD_RE` (commentaire mis à jour : les deux compteurs doivent coïncider). **+ teeth** : `test_tests_counts_non_single_space_def_separator` (fixture 3 méthodes : un-espace · deux-espaces · tab) — **prouvé mordant** : ancien regex `def ` compte **1** des 3 (loupe deux-espaces + tab) ; nouveau `def\s+` compte **3**. La fixture ASCII existante (`test_tests_counts_methods_and_thresholds` « 5 méthodes ») reste en un-espace → aucune régression. + +**Sortie inchangée (robustesse pure · #6 auto-vérifié).** Aucun module du dépôt n'ayant de `def` multi-espace/tab, `crit_tests` rend une évidence **identique pour les 24 modules** → `quality_report.json` **byte-identique** (rebuild `audit_4big_gen.py build` → `git diff --stat` **vide** ; `audit_4big` **s'auto-exclut** de son propre scoring — les modules qa scorés sont `qa-audit-5d`/`qa-acceptance`/`qa-regression`, pas lui). Le fix ne change **aucun score**. + +**Cascade d'artefacts (uniquement la matrice de régression · mémoire `artifact-reproducibility-gate`).** L'ajout du test augmente le décompte de la suite `audit_4big` : `regression_gen.py build` (plan) **puis** `run` (matrice) → `regression_run.json` **627 → 628 exécutés · 610 → 611 passés · 17 skippés · 0 échec**. Surfaces de prose gatées re-dérivées (flaggées RED par `check_readme_claims`, corrigées) : fiche `03_agents/qa/AGENT.md` (colonne `audit_4big` **36 → 37** ← `count_tests` ; agrégat **627/610 → 628/611**) + `03_agents/erpnext_backend/AGENT.md:40` (claim live **627 → 628**). Le `daily_reports/2026-08-11` (snapshot signé) et les logs sont des instantanés datés → laissés (`two-logging-channels`). + +**Classe désormais close (les 2 axes de la coïncidence des compteurs).** Après `072823` (char-class) + cette session (séparateur), `criteria._TEST_DEF_RE` et `discovery._TEST_METHOD_RE` sont **forme-identiques** ; l'axe fichiers (recursion) est non-occurrent et documenté laissé. **Pas de nouveau gate (#5)** : la divergence de compte est déjà **structurellement gatée** (`check_readme_claims` a immédiatement rougi la fiche QA dès que la matrice a bougé) et la teeth verrouille le comportement. Occurrence latente → correction de contrat de l'outil, pas de gate additionnel. + +**Vérif.** `./run_ci.sh` = **33 PASS · 0 FAIL · 0 SKIP** rétabli après régénération. **0 code moteur V18** (bloqué #6), **0 commande VPS** (#8), **0 API externe**. Fichiers : `qa/audit_4big/q4lib/criteria.py` (regex) + `qa/audit_4big/tests/test_audit_4big.py` (teeth) + `qa/regression/out/{regression_plan,regression_run,MANIFEST}.json` + `03_agents/qa/AGENT.md` + `03_agents/erpnext_backend/AGENT.md` + ce journal. `quality_report.json` **non touché** (auto-exclusion prouvée, `git diff --stat` vide). diff --git a/05_deliverables_mvp/qa/audit_4big/q4lib/criteria.py b/05_deliverables_mvp/qa/audit_4big/q4lib/criteria.py index 1cd04a4..b825453 100644 --- a/05_deliverables_mvp/qa/audit_4big/q4lib/criteria.py +++ b/05_deliverables_mvp/qa/audit_4big/q4lib/criteria.py @@ -15,7 +15,13 @@ import json import os import re -_TEST_DEF_RE = re.compile(r"^\s*def (test_\w+)\s*\(", re.MULTILINE) +# `\w` (Unicode) pour les identifiants accentués (`test_traçabilite_source`) ET +# `def\s+` (espacement libre) : `def test_x(` / `def\ttest_x(` sont du Python +# valide (PEP 8 recommande un espace mais le langage n'en impose pas). Forme +# STRICTEMENT identique à la source sœur `reglib/discovery.py:_TEST_METHOD_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\(") # 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é diff --git a/05_deliverables_mvp/qa/audit_4big/tests/test_audit_4big.py b/05_deliverables_mvp/qa/audit_4big/tests/test_audit_4big.py index 9a29a76..2410a8c 100644 --- a/05_deliverables_mvp/qa/audit_4big/tests/test_audit_4big.py +++ b/05_deliverables_mvp/qa/audit_4big/tests/test_audit_4big.py @@ -126,6 +126,27 @@ class CriteriaTest(unittest.TestCase): res = criteria.crit_tests(mod, 1) self.assertIn("2 méthodes", res["evidence"]) + def test_tests_counts_non_single_space_def_separator(self): + # Robustesse (complète l'alignement char-class du fix ASCII) : `def` et + # le nom de méthode peuvent être séparés par PLUSIEURS espaces ou une + # tabulation — du Python valide. Le littéral `def ` (un seul espace) + # loupait `def test_x(` / `def\ttest_x(` → sous-comptage silencieux et + # DÉSACCORD avec la source sœur reglib.discovery (`def\s+`), autorité de + # la colonne « Tests » des fiches. Les deux compteurs du MÊME concept + # doivent coïncider sur toute graphie valide (#6). + with tempfile.TemporaryDirectory() as d: + mod = os.path.join(d, "mod") + tdir = os.path.join(mod, "tests") + os.makedirs(tdir) + with open(os.path.join(tdir, "test_mod.py"), "w", + encoding="utf-8") as fh: + fh.write("import unittest\nclass T(unittest.TestCase):\n" + " def test_one_space(self):\n pass\n" + " def test_two_spaces(self):\n pass\n" + " def\ttest_tab(self):\n pass\n") + res = criteria.crit_tests(mod, 1) + self.assertIn("3 méthodes", res["evidence"]) + def test_cli_fails_without_entrypoint(self): with tempfile.TemporaryDirectory() as d: mod = _make_module(d, cli=False) diff --git a/05_deliverables_mvp/qa/regression/out/MANIFEST.json b/05_deliverables_mvp/qa/regression/out/MANIFEST.json index 5cd095d..45e34d3 100644 --- a/05_deliverables_mvp/qa/regression/out/MANIFEST.json +++ b/05_deliverables_mvp/qa/regression/out/MANIFEST.json @@ -5,7 +5,7 @@ "version": "1.0", "verdict": "PASS", "suites": 24, - "test_methods": 627, + "test_methods": 628, "coverage_ok": true, "artifacts": [ "regression_plan.json" diff --git a/05_deliverables_mvp/qa/regression/out/regression_plan.json b/05_deliverables_mvp/qa/regression/out/regression_plan.json index 221032f..d477358 100644 --- a/05_deliverables_mvp/qa/regression/out/regression_plan.json +++ b/05_deliverables_mvp/qa/regression/out/regression_plan.json @@ -211,7 +211,7 @@ "in_gate": true, "has_tests_dir": true, "test_files": 1, - "test_methods": 36 + "test_methods": 37 }, { "id": "qa.audit_5d", @@ -295,7 +295,7 @@ "totals": { "suites": 24, "test_files": 24, - "test_methods": 627, + "test_methods": 628, "min_methods": 10, "under_threshold": 0 }, diff --git a/05_deliverables_mvp/qa/regression/out/regression_run.json b/05_deliverables_mvp/qa/regression/out/regression_run.json index fa9ecdd..5f836bf 100644 --- a/05_deliverables_mvp/qa/regression/out/regression_run.json +++ b/05_deliverables_mvp/qa/regression/out/regression_run.json @@ -198,9 +198,9 @@ { "id": "qa.audit_4big", "path": "qa/audit_4big", - "expected_methods": 36, - "ran": 36, - "passed": 36, + "expected_methods": 37, + "ran": 37, + "passed": 37, "failures": 0, "errors": 0, "skipped": 0, @@ -296,8 +296,8 @@ "suites": 24, "green": 24, "red": 0, - "ran": 627, - "passed": 610, + "ran": 628, + "passed": 611, "failures": 0, "errors": 0, "skipped": 17