diff --git a/05_activity_log/2026-08-02.md b/05_activity_log/2026-08-02.md index 0c8539d..57deed6 100644 --- a/05_activity_log/2026-08-02.md +++ b/05_activity_log/2026-08-02.md @@ -1,5 +1,55 @@ # Activity log · 2026-08-02 · Claude Code DTP Worker +## Sprint 8 · buffer · Fiscal e-CF — ancrage du CONTRAT DE FORMAT e-NCF dans le COMPOSEUR `ncf.py` (CODE) + son ORACLE de test + +**Contexte** — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention de +chiffres »). Le bloc « Fiscal · e-CF DGII · CONTRAT DE FORMAT » existant ancre le +format de l'identifiant fiscal DGII (`E + tipoeCF(2) + secuencia(10)` = 13, +Ley 32-23) sur le SEUL bloc `e_ncf` byte-gaté de `out/ecf_plan.json` (dérivé de +`ecf_spec.json`) et couvre **4 surfaces de PROSE** : README ×3 + fiche +`erpnext_backend`. Mais le **composeur d'exécution lui-même** — `ecflib/ncf.py`, +le code Frappe qui VALIDE et COMPOSE réellement le numéro e-NCF côté VPS/Compupar +— porte **TROIS copies INDÉPENDANTES du format, AUCUNE dérivée de l'artefact**, + +un oracle de test qui les bénit, tous **NON gatés** : + +1. `ENCF_RE = re.compile(r"^E([0-9]{2})([0-9]{10})$")` (l.26) — la **regex de + VALIDATION** (cœur de `is_valid_encf`/`parse_encf`) ; +2. `FORMULE = "E + tipoeCF(2) + secuencia(10)"` (l.27) — la chaîne **publiée par + `compose_encf`** (renvoyée au caller VPS) ; +3. docstring l.8 « `"E" + tipoeCF (2 chiffres) + secuencia (10 chiffres) → 13 + caracteres` » + commentaire l.25 « Ley 32-23 : E + tipo(2) + secuencia(10) = + 13 » (deux proses-en-code) ; +4. `tests/test_ecf_dgii.py:246` — oracle hardcodé `assertEqual(…["formule"], + "E + tipoeCF(2) + secuencia(10)")`. + +**Le « vert trompeur »** — un rango DGII à 12 chiffres (évènement ops réel, cité +dans le commentaire du gate existant) régénère `ecf_spec→ecf_plan`, donc les 4 +proses gatées SUIVENT. Mais `ENCF_RE` reste `{2}{10}` : `compose_encf` zfill à 10 +et `is_valid_encf` **REJETTE** un e-NCF désormais valide → **bug d'exécution du +composeur fiscal**, pendant que la suite `tests/` reste **VERTE** (son oracle == +`FORMULE` périmée, self-consistante). Ni `check_artifacts` (`ncf.py` n'est pas un +`out/*.json`) ni `tests/` (teste les FONCTIONS, pas leur ANCRAGE à l'artefact) ne +mordent. Même patron que l'oracle/formateur du Publiciste déjà gaté. + +**Gate ajouté** (`ci/check_readme_claims.sh`, extension du bloc e-NCF · surfaces +5→9) — RÉUTILISE `prefix/tipo_len/seq_len/total` déjà dérivés du bloc `e_ncf` +byte-gaté + le helper `ecf_check` (zéro re-dérivation du format #6). Ancre les 5 +surfaces CODE/test au SEUL artefact : (5) `ENCF_RE`, (6) `FORMULE`, (7) docstring, +(8) commentaire, (9) oracle de test. Une surface absente échoue AUSSI (INTROUVABLE +· anti-évaporation #6). + +**Vérif** — 5 checks verts sur l'arbre propre. **7 morsures** adversariales : +M1 `ENCF_RE {10}→{12}` · M2 `FORMULE secuencia(10)→(12)` · M3 docstring `10→12 +chiffres` · M4 commentaire `secuencia(10)→(12)` · M5 oracle test `(10)→(12)` · +**M6 SILENT-GREEN — l'ARTEFACT passe à seq=12 pendant que `ncf.py` reste à 10 ⇒ +les 5 surfaces mordent TOUTES (preuve que l'autorité est l'artefact byte-gaté, pas +la cohérence interne du composeur)** · M7 `FORMULE` renommée ⇒ INTROUVABLE. Les +**39 tests unittest** e-CF restent verts (composeur inchangé). Gates re-verts : +`check_artifacts` · `check_ci_integrity` · `check_docs` · `check_readme_claims` · +`check_regression` · `guard_constraints` tous exit 0. + +--- + ## Sprint 8 · buffer · CRM Commissions — gate d'IDENTITÉ du COMPTE D'INVARIANTS de `_validate` (7ᵉ instance du ledger) **Contexte** — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention de diff --git a/ci/check_readme_claims.sh b/ci/check_readme_claims.sh index fbe207c..5c2e38d 100755 --- a/ci/check_readme_claims.sh +++ b/ci/check_readme_claims.sh @@ -3655,6 +3655,60 @@ if encf is not None: ecf_check("fiche erpnext_backend", ECF_FI, ecf_fi, re.search(r"`([A-Z])\s*\+\s*tipoeCF\((\d+)\)\s*\+\s*" r"secuencia\((\d+)\)`", ecf_fi), False) + # ------------------------------------------------------------------ + # 5) 6) 7) 8) 9) — le MÊME contrat de format dans le COMPOSEUR ncf.py + # (CODE d'exécution, PAS de la prose) + son ORACLE de test. + # `ecflib/ncf.py` porte TROIS copies INDÉPENDANTES du format, AUCUNE + # dérivée de l'artefact : la regex de VALIDATION `ENCF_RE` (cœur de + # is_valid_encf/parse_encf), la chaîne `FORMULE` publiée par + # compose_encf (donc RENVOYÉE au caller VPS/Compupar), et deux + # proses-en-code (docstring + commentaire Ley 32-23). En prime, + # `tests/test_ecf_dgii.py:246` HARDCODE l'oracle « FORMULE ». Le bloc + # e-NCF ci-dessus ne gate QUE la prose (README ×3 + fiche). Or un + # rango DGII à 12 chiffres régénère ecf_spec→ecf_plan (donc les 4 + # proses gatées ci-dessus SUIVENT), MAIS laisse ENCF_RE à `{2}{10}` : + # compose_encf zfill à 10 et is_valid_encf REJETTE un e-NCF désormais + # valide → BUG d'exécution du composeur fiscal, pendant que la suite + # tests/ reste VERTE (son oracle == FORMULE périmée, self-consistante). + # « Vert trompeur » que ni check_artifacts (ncf.py n'est pas un + # out/*.json) ni tests/ (teste les FONCTIONS, pas leur ancrage) ne + # mordent. Même patron que l'oracle/formateur du Publiciste. On ANCRE + # ces surfaces CODE au SEUL bloc e_ncf byte-gaté (réutilise + # prefix/tipo/seq/total ci-dessus · zéro re-dérivation du format #6). + ECF_NCF = f"{D}/fiscal/ecf_dgii/ecflib/ncf.py" + ECF_TEST = f"{D}/fiscal/ecf_dgii/tests/test_ecf_dgii.py" + ecf_ncf = read_norm(ECF_NCF) + if ecf_ncf is None: + bad(f"Fiscal · composeur {ECF_NCF} illisible → format e-NCF non ancré") + else: + # 5) ENCF_RE — la regex de VALIDATION (prefix + 2 segments de chiffres) + ecf_check("composeur ncf.py · ENCF_RE (validateur)", ECF_NCF, ecf_ncf, + re.search(r"ENCF_RE\s*=\s*re\.compile\(\s*r?[\"']\^" + r"([A-Z])\(\[0-9\]\{(\d+)\}\)\(\[0-9\]\{(\d+)\}\)\$", + ecf_ncf), False) + # 6) FORMULE — la chaîne publiée par compose_encf (renvoyée au VPS) + ecf_check("composeur ncf.py · FORMULE (publiée)", ECF_NCF, ecf_ncf, + re.search(r"FORMULE\s*=\s*[\"']([A-Z])\s*\+\s*tipoeCF\((\d+)\)" + r"\s*\+\s*secuencia\((\d+)\)[\"']", ecf_ncf), False) + # 7) docstring ncf.py — « "E" + tipoeCF (2 chiffres) + secuencia (10 chiffres) → 13 caracteres » + ecf_check("composeur ncf.py · docstring", ECF_NCF, ecf_ncf, + re.search(r'e-NCF\s*=\s*"([A-Z])"\s*\+\s*tipoeCF\s*\((\d+)' + r"\s*chiffres?\)\s*\+\s*secuencia\s*\((\d+)\s*chiffres?\)" + r"\s*\S+\s*(\d+)\s*caracteres?", ecf_ncf), True) + # 8) commentaire ncf.py — « E + tipo(2) + secuencia(10) = 13 caracteres » + ecf_check("composeur ncf.py · commentaire Ley 32-23", ECF_NCF, ecf_ncf, + re.search(r"Format e-NCF[^:]*:\s*([A-Z])\s*\+\s*tipo\((\d+)\)" + r"\s*\+\s*secuencia\((\d+)\)\s*=\s*(\d+)\s*caracteres?", + ecf_ncf), True) + # 9) oracle de test — la RAISON pour laquelle la dérive resterait VERTE + ecf_test = read_norm(ECF_TEST) + if ecf_test is None: + bad(f"Fiscal · oracle {ECF_TEST} illisible → FORMULE de test non ancrée") + else: + ecf_check("oracle test_ecf_dgii.py · formule", ECF_TEST, ecf_test, + re.search(r"\[[\"']formule[\"']\]\s*,\s*[\"']([A-Z])\s*\+" + r"\s*tipoeCF\((\d+)\)\s*\+\s*secuencia\((\d+)\)[\"']", + ecf_test), False) # ============================================================================ # Fiscal · e-CF DGII (2e surface du MÊME README) · la CROSS-COHÉRENCE