From 0790fa7811b7616a8054dbb750949d3bdb64afd0 Mon Sep 17 00:00:00 2001 From: Claude Code DTP Worker Date: Fri, 31 Jul 2026 20:38:52 +0000 Subject: [PATCH] =?UTF-8?q?[DTP-Worker]=20Sprint=208=20=C2=B7=20buffer=20?= =?UTF-8?q?=C2=B7=20Fiscal/ecf=5Fdgii=20(2e=20surface)=20:=20la=20CROSS-CO?= =?UTF-8?q?H=C3=89RENCE=20e-CF=20=E2=86=94=20workflow=20=E2=86=94=20DocTyp?= =?UTF-8?q?e=20=E2=86=94=20RBAC=20=E2=80=94=20le=20C=C5=92UR=20du=20livrab?= =?UTF-8?q?le=20=E2=80=94=20=C3=A9num=C3=A9rait=20EN=20PROSE=20trois=20don?= =?UTF-8?q?n=C3=A9es=20data-derived=20(=C3=A9tats=20qui=20=C3=89METTENT=20?= =?UTF-8?q?un=20e-CF=20=C2=B7=20champs=20Currency=20de=20base=20=C2=B7=20F?= =?UTF-8?q?ormaPago=20d=C3=A9faut=20ancr=C3=A9e=20sur=20CLAUDE.md=20#10/Ca?= =?UTF-8?q?rdnet)=20sans=20AUCUN=20gate=20d'IDENTIT=C3=89.=20Le=20bloc=20e?= =?UTF-8?q?-NCF=20existant=20ne=20gate=20QUE=20le=20FORMAT=20de=20l'identi?= =?UTF-8?q?fiant=20(E+tipo(2)+seq(10)=3D13)=20=E2=80=94=20jamais=20ces=20t?= =?UTF-8?q?rois=20ensembles/valeur.=20Ils=20sont=20DATA-DERIVED=20de=20out?= =?UTF-8?q?/ecf=5Fplan.json=20(byte-gat=C3=A9=20par=20check=5Fartifacts)?= =?UTF-8?q?=20:=20README:48=20=C2=AB=20seulement=20r=C3=A9servation=20et?= =?UTF-8?q?=20contrat=20=C2=BB=20=3D=20{emission=5Fevents[].update=5Fvalue?= =?UTF-8?q?}=20=C2=B7=20README:50=20=C2=AB=20(montant=5Freservation,=20mon?= =?UTF-8?q?tant=5Fcontrat)=20=C2=BB=20=3D=20{emission=5Fevents[].base=5Ffi?= =?UTF-8?q?eld}=20=C2=B7=20README:59=20=C2=AB=20FormaPago=20d=C3=A9faut=20?= =?UTF-8?q?=3D=203=20(Tarjeta)=20=E2=80=A6=20via=20Cardnet=20(#10)=20?= =?UTF-8?q?=C2=BB=20=3D=20forma=5Fpago=5Fdefaut=20(ancr=C3=A9=20sur=20CLAU?= =?UTF-8?q?DE.md=20#10=20:=20Cardnet=20=3D=20carte,=20pas=20Stripe).=20PI?= =?UTF-8?q?=C3=88GE=20:=20check=5Fartifacts=20ne=20prouve=20QUE=20ecf=5Fpl?= =?UTF-8?q?an=3D=3Dbuild=20(byte-for-byte)=20et=20le=20bloc=20racine=20ne?= =?UTF-8?q?=20gate=20QUE=20le=20compte=20de=20tests=20e-CF=20(=C2=AB=2039?= =?UTF-8?q?=20=C2=BB)=20=E2=86=92=20AJOUTER=20un=20=C3=A9v=C3=A8nement=20d?= =?UTF-8?q?'=C3=A9mission=20(facturer=20un=20devis)=20=C2=B7=20en=20RETIRE?= =?UTF-8?q?R=20un=20=C2=B7=20RENOMMER=20un=20base=5Ffield=20=C2=B7=20CHANG?= =?UTF-8?q?ER=20forma=5Fpago=5Fdefaut=20(Cardnet=20abandonn=C3=A9=20?= =?UTF-8?q?=E2=86=92=20code=202=20Transfert)=20laissait=20la=20prose=20p?= =?UTF-8?q?=C3=A9rim=C3=A9e=20pendant=20que=20l'artefact=20dit=20autre=20c?= =?UTF-8?q?hose=20=E2=86=92=20l'agent=20ERPNext=20Backend=20c=C3=A2blerait?= =?UTF-8?q?=20le=20mauvais=20=C3=A9v=C3=A8nement=20/=20la=20mauvaise=20For?= =?UTF-8?q?maPago=20(le=20risque=20m=C3=AAme=20que=20la=20cross-coh=C3=A9r?= =?UTF-8?q?ence=20veut=20pr=C3=A9venir)=20=E2=80=94=20=C2=AB=20vert=20trom?= =?UTF-8?q?peur=20=C2=BB=20qu'aucune=20suite=20tests/=20(qui=20teste=20des?= =?UTF-8?q?=20FONCTIONS=20ncf/r=C3=A9solution,=20pas=20la=20prose)=20n'att?= =?UTF-8?q?rape.=20M=C3=AAme=20patron=20EXACT=20que=20la=20cross-coh=C3=A9?= =?UTF-8?q?rence=20CRM/Commissions=20d=C3=A9j=C3=A0=20gat=C3=A9e=20(base?= =?UTF-8?q?=5Ffield=20+=20=C3=A9tats=20soumis).=20Nouveau=20bloc=20=C2=AB?= =?UTF-8?q?=20Fiscal=20cross-coh=C3=A9rence=20=C2=BB=20dans=20ci/check=5Fr?= =?UTF-8?q?eadme=5Fclaims.sh=20(apr=C3=A8s=20le=20bloc=20e-NCF)=20:=20(1)?= =?UTF-8?q?=20=C3=A9tats=20=C3=A9metteurs=20recomput=C3=A9s=20de=20emissio?= =?UTF-8?q?n=5Fevents[].update=5Fvalue,=20prose=20exig=C3=A9e=20EXACTE=20(?= =?UTF-8?q?set-diff=20=C2=B7=20accents=20NFKD)=20;=20(2)=20champs=20de=20b?= =?UTF-8?q?ase=20recomput=C3=A9s=20de=20emission=5Fevents[].base=5Ffield,?= =?UTF-8?q?=20prose=20EXACTE=20;=20(3)=20FormaPago=20d=C3=A9faut=20=3D=20f?= =?UTF-8?q?orma=5Fpago=5Fdefaut.code=20+=20libell=C3=A9=20(=E2=8A=86=20cat?= =?UTF-8?q?alogue)=20+=20ancrage=20Cardnet=20=C3=A0=20proximit=C3=A9.=20Co?= =?UTF-8?q?h=C3=A9rences=20crois=C3=A9es=20en=20bonus=20(mordent=20un=20pl?= =?UTF-8?q?an=20INTERNEMENT=20incoh=C3=A9rent)=20:=20forma=5Fpago=5Fdefaut?= =?UTF-8?q?.code=20=E2=88=88=20catalogue=20formas=5Fpago=20=C2=B7=20libell?= =?UTF-8?q?=C3=A9=20prose=20=E2=8A=86=20libell=C3=A9=20catalogue=20du=20co?= =?UTF-8?q?de=20=C2=B7=20ensembles=20NON=20VIDES.=20Un=20claim=20absent=20?= =?UTF-8?q?=C3=A9choue=20AUSSI.=207=20morsures=20v=C3=A9rifi=C3=A9es=20:?= =?UTF-8?q?=20prose=20retire=20contrat=20des=20=C3=A9metteurs=20(absents?= =?UTF-8?q?=3D[contrat])=20=C2=B7=20prose=20renomme=20montant=5Fcontrat?= =?UTF-8?q?=E2=86=92montant=5Ffinal=20(absents=20+=20en=20trop)=20=C2=B7?= =?UTF-8?q?=20prose=20FormaPago=203=E2=86=922=20(drift=20artefact)=20?= =?UTF-8?q?=C2=B7=20artefact=20forma=5Fpago=5Fdefaut.code=203=E2=86=922=20?= =?UTF-8?q?(prose=20p=C3=A9rim=C3=A9e)=20=C2=B7=20artefact=20ajoute=20l'?= =?UTF-8?q?=C3=A9v=C3=A8nement=20devis=20(=C3=A9tats=20ET=20base=5Ffield?= =?UTF-8?q?=20mordus=20=C2=B7=20absents=3D[devis])=20=C2=B7=20FormaPago=20?= =?UTF-8?q?prose=20supprim=C3=A9e=20(INTROUVABLE)=20=C2=B7=20artefact=20co?= =?UTF-8?q?de=E2=86=9299=20hors=20catalogue=20(incoh=C3=A9rence=20interne)?= =?UTF-8?q?=20;=20restaur=C3=A9=20=3D=20green=20:=20=C3=A9metteurs=20[cont?= =?UTF-8?q?rat,reservation]=20=C2=B7=20base=20[montant=5Fcontrat,montant?= =?UTF-8?q?=5Freservation]=20=C2=B7=20FormaPago=203=20(Tarjeta)=20ancr?= =?UTF-8?q?=C3=A9=20Cardnet=20=C2=B7=20exit=200.=20=C3=89tat=20courant=20:?= =?UTF-8?q?=20aucune=20valeur=20p=C3=A9rim=C3=A9e=20(anti-invention=20#6,?= =?UTF-8?q?=20rien=20=C3=A0=20r=C3=A9=C3=A9crire)=20=E2=80=94=20le=20d?= =?UTF-8?q?=C3=A9faut=20est=20la=20surface=20ungated.=20ci/README.md=20(ta?= =?UTF-8?q?ble=20+=20d=C3=A9tail=20=C2=AB=202e=20surface=20e-CF=20DGII=20?= =?UTF-8?q?=C2=BB)=20mis=20=C3=A0=20jour=20=C2=B7=20working=20tree=20byte-?= =?UTF-8?q?restaur=C3=A9=20(git=20clean)=20=C2=B7=207=20gates=20re-verts.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- 05_activity_log/2026-07-31.md | 48 +++++++++++++++ ci/README.md | 29 +++++++++- ci/check_readme_claims.sh | 106 ++++++++++++++++++++++++++++++++++ 3 files changed, 182 insertions(+), 1 deletion(-) diff --git a/05_activity_log/2026-07-31.md b/05_activity_log/2026-07-31.md index e4e7752..7738418 100644 --- a/05_activity_log/2026-07-31.md +++ b/05_activity_log/2026-07-31.md @@ -1,5 +1,53 @@ # Activity Log · 2026-07-31 · Claude Code DTP +## Session `20260731_203134` · Buffer S8 · Domaine Fiscal/ecf_dgii (2e surface) : la **CROSS-COHÉRENCE e-CF ↔ workflow ↔ DocType ↔ RBAC** — le CŒUR du livrable — énumère EN PROSE trois données data-derived (états qui émettent un e-CF, champs Currency de base, **FormaPago défaut** ancrée sur CLAUDE.md #10/Cardnet) **sans aucun gate d'IDENTITÉ**. Le bloc e-NCF existant ne gate QUE le FORMAT de l'identifiant. + +**Tâche** : **Sprint 8 · buffer** (DevOps CI/CD · QA). Roadmap fonctionnellement +close ; poursuite de la série anti-dérive (CLAUDE.md #6). Même patron EXACT que la +cross-cohérence CRM/Commissions déjà gatée (base_field + états soumis) — appliqué à +la 2e surface data-derived du README `fiscal/ecf_dgii`, jamais gatée. + +**Dérive silencieuse fermée** : +- Source faisant autorité : `05_deliverables_mvp/fiscal/ecf_dgii/out/ecf_plan.json` + (byte-gaté par `check_artifacts`). Trois claims du README étaient recopiés à la + main sans recompute : README:48 « seulement **réservation et contrat** » = + `{emission_events[].update_value}` · README:50 « (`montant_reservation`, + `montant_contrat`) » = `{emission_events[].base_field}` · README:59 « **FormaPago + défaut = 3 (Tarjeta)** … via **Cardnet** (#10) » = `forma_pago_defaut` (ancrée sur + CLAUDE.md #10 — Cardnet = paiement carte, pas Stripe). +- Piège : `check_artifacts` ne prouve QUE `ecf_plan == build` (byte-for-byte) et le + bloc racine ne gate QUE le compte de tests e-CF (« 39 ») ; AJOUTER un évènement + d'émission (facturer un devis), en RETIRER un, RENOMMER un `base_field` ou CHANGER + `forma_pago_defaut` (Cardnet abandonné → code 2 Transfert) laissait la prose périmée + pendant que l'artefact dit autre chose → l'agent ERPNext Backend câblerait le + mauvais évènement / la mauvaise FormaPago (le risque même que la cross-cohérence + veut prévenir). Aucune suite `tests/` (qui teste les FONCTIONS `ncf`/résolution, + pas la prose) n'attrape ce « vert trompeur ». + +**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « Fiscal cross-cohérence » +après le bloc e-NCF) : (1) états émetteurs recomputés de `emission_events[].update_value`, +prose exigée EXACTE (set-diff · accents normalisés NFKD) ; (2) champs de base recomputés +de `emission_events[].base_field`, prose exigée EXACTE ; (3) FormaPago défaut = +`forma_pago_defaut.code` + libellé (⊆ catalogue) + ancrage Cardnet exigé à proximité. +Cohérences croisées en bonus (mordent un plan INTERNEMENT incohérent) : +`forma_pago_defaut.code` ∈ catalogue `formas_pago` · libellé prose ⊆ libellé catalogue · +ensembles non vides. Un claim absent échoue AUSSI (traçabilité). + +**7 morsures vérifiées** : prose retire `contrat` des émetteurs (absents=[contrat]) · +prose renomme `montant_contrat→montant_final` (absents + en trop) · prose FormaPago +`3→2` (drift artefact) · artefact `forma_pago_defaut.code 3→2` (prose périmée) · +artefact ajoute l'évènement `devis` (états ET base_field mordus, absents=[devis]) · +FormaPago prose supprimée (INTROUVABLE) · artefact `code→99` hors catalogue +(incohérence interne) ; restauré = green : émetteurs [contrat,reservation] · base +[montant_contrat,montant_reservation] · FormaPago 3 (Tarjeta) ancré Cardnet · exit 0. +Working tree byte-restauré (git clean) · **7 gates re-verts**. + +- État courant : aucune valeur périmée (anti-invention #6, rien à réécrire) — le + défaut est la surface ungated. `ci/README.md` (table + détail « 2e surface e-CF + DGII ») mis à jour. +- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo). +- **Auto-score 4Big** : 96/100. + ## Session `20260731_200130` · Buffer S8 · Domaine Faisabilité/generator : les **paramètres canoniques** du générateur 4 volets (« `3 %` édition · `8.5 %` marketing · `52 %` point d'équilibre · `USD+DOP` · `Cardnet` · `Letter US` ») — la garantie anti-invention (#6) du module, imposée à CHAQUE faisabilité — étaient **RECOPIÉS** dans `genlib/model.py::CANONICAL` (dict émis dans chaque rendu, **hors `out/`** donc invisible à check_artifacts) **et** dans la prose README:28, **sans aucun ancrage** à leur source faisant autorité : les contraintes NON-NÉGOCIABLES **#9/#10 de CLAUDE.md**. **Tâche** : **Sprint 8 · buffer** (DevOps CI/CD · QA). Roadmap fonctionnellement diff --git a/ci/README.md b/ci/README.md index f06a8b2..3391a10 100644 --- a/ci/README.md +++ b/ci/README.md @@ -20,7 +20,7 @@ et manuellement (`workflow_dispatch`). Jobs statiques (+ une suite `unittest` pa | `check-artifacts` | `ci/check_artifacts.sh` | Reproductibilité : chaque `out/*.json` versionné == build frais | ✅ oui | | `check-regression` | `ci/check_regression.sh` | Fraîcheur : `qa/regression/out/regression_run.json` (`run`) == run frais + verdict PASS | ✅ oui | | `check-ci-integrity` | `ci/check_ci_integrity.sh` | Intégrité du câblage : `gate.needs` == tous les jobs non-manuels · chaque `ci/*.sh` câblé au gate | ✅ oui | -| `check-readme-claims` | `ci/check_readme_claims.sh` | Intégrité des chiffres des docs d'entrée : chaque nombre de `README.md` « État courant », de la fiche QA « Verdict agrégé » **et** de la colonne « Tests » par suite des tables de fiches **et** des agrégats en prose (Total CRM / RBAC / e-CF) **et** des comptes par workspace des portails (cartes/liens/rôles, ×2 docs) **et** des comptes RBAC des 3 volets (table Nb rôles/profil, ligne agrégée apply_plan, ventilation userperm) **et** des comptes de mots-clés SEO (total + fr/en/es, ×2 docs) **et** du pipeline vente CRM (états/transitions, ×2 docs + énumération des transitions à séparation des pouvoirs `{action | allow_self_approval=0}` recomputée de `workflow.json`) **et** de l'app mobile Expo (onglets/rôles/langues/identifiants a_confirmer + énumération ordonnée des onglets recomputée de `role_navigation`, bijection onglet⇔portail) **et** du DocType CONFOTUR (champs/données/sections/rôles/évènements de dépôt, ×2 docs) **et** de l'audit 5D (contrôles/dimensions ×2 docs + ventilation verdict pass/fail/à-confirmer + énumération des open items `control→dimension→propriétaire` recomputée de `audit_report.open_items`) **et** du run-book de déploiement VPS (phases ×2 dans la fiche devops + confirmations préalables count & énumération dans le README) **et** du run-sheet de démo (nombre de modules cités dans le diagramme du README) **et** du montage Chat OTOIA par portail (`Custom Block`/configs runtime/portails, tous = 5 aujourd'hui, ×2 docs) **et** du module de recette `qa/acceptance` (couple livrables de sprint + métriques succès MVP, ×2 docs de module au-delà du README d'entrée) **et** du barème de l'auditeur 4Big (nombre de critères en prose + poids par critère de la table + « generator (N critères) », recomputés de `quality_report.criteria`) **et** de la carte de renormalisation par archétype de l'auditeur 4Big (critères EXCLUS de `data_room`/`parser`/`contract`, recomputés des `checks` par archétype de `quality_report.modules`) **et** du contrat de format de l'e-NCF fiscal (`E + tipoeCF(2) + secuencia(10) = 13` : préfixe/longueurs des segments/longueur totale, ×3 README + fiche `erpnext_backend`, recomputés du bloc `e_ncf` d'`ecf_plan.json` + cohérences croisées `1+tipo+seq==longueur` & `|regex digits|==tipo+seq`) **et** de la cross-cohérence du barème de commissions `crm/commissions` (états brouillon exclus recomputés de `{doc_status=0}` de `workflow.json` + champs de base recomputés de `{base_field}` de `commission_plan.json` + invariant 3 « commissionne seulement des états soumis ») **et** du catalogue RBAC `rbac/fixtures_gen` (DocTypes `custom` à créer énumérés == `MANIFEST.custom_doctypes_a_confirmer` + séparation des pouvoirs : le rôle unique porteur de `set_user_permissions` == `custom_docperm.json`) **et** de la composition du graphe schema.org SEO (les `@type` du `@graph` — `Organization` racine + une `Residence` par projet — recomputés de `seo_schema_org.json` + bijection listing⇔projet & absence d'`offers`) **et** de la carte hreflang SEO (les langues de contenu `alternate` `FR/EN/ES` + `x-default` recomputées de `seo_hreflang.json` + cohérences croisées : carte uniforme sur toutes les pages · `x-default` présent partout · langue par défaut ∈ langues de contenu · `x-default`.href == `canonical` == href de la langue par défaut) **et** du catalogue de projets du DocType `crm/dossier_vente` (le Select `projet` — les 9 projets `P01..P09` avec libellés — **ancré sur** la section « ## Projets » de `CLAUDE.md`, recomputé de CLAUDE.md : ensemble + ordre == options du DocType byte-gaté, plage `P01..P09` + mot d'ancrage dans le README, catalogue contigu/sans-doublon en bonus) **et** du catalogue des DocTypes `custom` à créer du portail `frontend/portails` (2e surface : le §hand-off VPS énumère `CONFOTUR Application`/`Faisabilité`/`Publiciste Log` == `MANIFEST.doctypes_custom_a_creer` recomputé, set-diff normalisé + cohérences croisées : catalogue trié/sans-doublon/non-vide · égal à l'UNION des `doctypes_custom` par workspace) **et** du classement métier/technique des `Role Profile` de `rbac/roleprofile_gen` (2e surface : la colonne « Type » de la table — auparavant un WILDCARD — recompute le flag `metier` par profil de `MANIFEST.profiles` + prose « N profils (X métier + Y technique) » == `counts.role_profiles/portails_metier/portails_techniques` + identité d'ensemble : lignes de profil de la table == `|profiles|` (aucun profil fantôme) + cohérences croisées : `portails_metier + portails_techniques == role_profiles` et recomptent les flags réels) **et** des paramètres canoniques du générateur de faisabilité `faisabilite/generator` (**ancrés sur** CLAUDE.md #9/#10 : les 6 valeurs `3 %`/`8.5 %`/`52 %`/`USD+DOP`/`Letter US`/`Cardnet` recomputées des contraintes #9/#10 de `CLAUDE.md` puis exigées EXACTES dans `genlib/model.py::CANONICAL` — le dict émis dans chaque rendu, hors `out/` donc invisible à check_artifacts — ET dans la prose README, set-diff normalisé) == artefact cité (#6) | ✅ oui | +| `check-readme-claims` | `ci/check_readme_claims.sh` | Intégrité des chiffres des docs d'entrée : chaque nombre de `README.md` « État courant », de la fiche QA « Verdict agrégé » **et** de la colonne « Tests » par suite des tables de fiches **et** des agrégats en prose (Total CRM / RBAC / e-CF) **et** des comptes par workspace des portails (cartes/liens/rôles, ×2 docs) **et** des comptes RBAC des 3 volets (table Nb rôles/profil, ligne agrégée apply_plan, ventilation userperm) **et** des comptes de mots-clés SEO (total + fr/en/es, ×2 docs) **et** du pipeline vente CRM (états/transitions, ×2 docs + énumération des transitions à séparation des pouvoirs `{action | allow_self_approval=0}` recomputée de `workflow.json`) **et** de l'app mobile Expo (onglets/rôles/langues/identifiants a_confirmer + énumération ordonnée des onglets recomputée de `role_navigation`, bijection onglet⇔portail) **et** du DocType CONFOTUR (champs/données/sections/rôles/évènements de dépôt, ×2 docs) **et** de l'audit 5D (contrôles/dimensions ×2 docs + ventilation verdict pass/fail/à-confirmer + énumération des open items `control→dimension→propriétaire` recomputée de `audit_report.open_items`) **et** du run-book de déploiement VPS (phases ×2 dans la fiche devops + confirmations préalables count & énumération dans le README) **et** du run-sheet de démo (nombre de modules cités dans le diagramme du README) **et** du montage Chat OTOIA par portail (`Custom Block`/configs runtime/portails, tous = 5 aujourd'hui, ×2 docs) **et** du module de recette `qa/acceptance` (couple livrables de sprint + métriques succès MVP, ×2 docs de module au-delà du README d'entrée) **et** du barème de l'auditeur 4Big (nombre de critères en prose + poids par critère de la table + « generator (N critères) », recomputés de `quality_report.criteria`) **et** de la carte de renormalisation par archétype de l'auditeur 4Big (critères EXCLUS de `data_room`/`parser`/`contract`, recomputés des `checks` par archétype de `quality_report.modules`) **et** du contrat de format de l'e-NCF fiscal (`E + tipoeCF(2) + secuencia(10) = 13` : préfixe/longueurs des segments/longueur totale, ×3 README + fiche `erpnext_backend`, recomputés du bloc `e_ncf` d'`ecf_plan.json` + cohérences croisées `1+tipo+seq==longueur` & `|regex digits|==tipo+seq`) **et** de la cross-cohérence e-CF DGII `fiscal/ecf_dgii` (2e surface : états émetteurs recomputés de `{emission_events[].update_value}` + champs de base recomputés de `{emission_events[].base_field}` + FormaPago défaut « 3 (Tarjeta) » recomputée de `forma_pago_defaut` **ancrée sur CLAUDE.md #10** (Cardnet) + cohérences croisées : `forma_pago_defaut.code` ∈ catalogue `formas_pago` · libellé prose ⊆ libellé catalogue · ensembles non vides) **et** de la cross-cohérence du barème de commissions `crm/commissions` (états brouillon exclus recomputés de `{doc_status=0}` de `workflow.json` + champs de base recomputés de `{base_field}` de `commission_plan.json` + invariant 3 « commissionne seulement des états soumis ») **et** du catalogue RBAC `rbac/fixtures_gen` (DocTypes `custom` à créer énumérés == `MANIFEST.custom_doctypes_a_confirmer` + séparation des pouvoirs : le rôle unique porteur de `set_user_permissions` == `custom_docperm.json`) **et** de la composition du graphe schema.org SEO (les `@type` du `@graph` — `Organization` racine + une `Residence` par projet — recomputés de `seo_schema_org.json` + bijection listing⇔projet & absence d'`offers`) **et** de la carte hreflang SEO (les langues de contenu `alternate` `FR/EN/ES` + `x-default` recomputées de `seo_hreflang.json` + cohérences croisées : carte uniforme sur toutes les pages · `x-default` présent partout · langue par défaut ∈ langues de contenu · `x-default`.href == `canonical` == href de la langue par défaut) **et** du catalogue de projets du DocType `crm/dossier_vente` (le Select `projet` — les 9 projets `P01..P09` avec libellés — **ancré sur** la section « ## Projets » de `CLAUDE.md`, recomputé de CLAUDE.md : ensemble + ordre == options du DocType byte-gaté, plage `P01..P09` + mot d'ancrage dans le README, catalogue contigu/sans-doublon en bonus) **et** du catalogue des DocTypes `custom` à créer du portail `frontend/portails` (2e surface : le §hand-off VPS énumère `CONFOTUR Application`/`Faisabilité`/`Publiciste Log` == `MANIFEST.doctypes_custom_a_creer` recomputé, set-diff normalisé + cohérences croisées : catalogue trié/sans-doublon/non-vide · égal à l'UNION des `doctypes_custom` par workspace) **et** du classement métier/technique des `Role Profile` de `rbac/roleprofile_gen` (2e surface : la colonne « Type » de la table — auparavant un WILDCARD — recompute le flag `metier` par profil de `MANIFEST.profiles` + prose « N profils (X métier + Y technique) » == `counts.role_profiles/portails_metier/portails_techniques` + identité d'ensemble : lignes de profil de la table == `|profiles|` (aucun profil fantôme) + cohérences croisées : `portails_metier + portails_techniques == role_profiles` et recomptent les flags réels) **et** des paramètres canoniques du générateur de faisabilité `faisabilite/generator` (**ancrés sur** CLAUDE.md #9/#10 : les 6 valeurs `3 %`/`8.5 %`/`52 %`/`USD+DOP`/`Letter US`/`Cardnet` recomputées des contraintes #9/#10 de `CLAUDE.md` puis exigées EXACTES dans `genlib/model.py::CANONICAL` — le dict émis dans chaque rendu, hors `out/` donc invisible à check_artifacts — ET dans la prose README, set-diff normalisé) == artefact cité (#6) | ✅ oui | | `gate` | — | Agrégat vert = gate qualité 4Big franchi | ✅ oui | Aucune dépendance réseau/marketplace hors `actions/checkout`. Tout tourne avec @@ -458,6 +458,33 @@ fiche `erpnext_backend` `secuencia 10→9` · composition README supprimée (INTROUVABLE) ; plus la morsure d'incohérence d'artefact (`regex` désaligné du segment) ; restauré = vert. +Même classe enfin sur la **cross-cohérence e-CF DGII** (2e surface du MÊME README +`fiscal/ecf_dgii`) — le CŒUR du livrable. Le bloc e-NCF ci-dessus ne gate que le +**format** de l'identifiant ; la section « Cross-cohérence e-CF ↔ workflow ↔ DocType +↔ RBAC » énumère **à la main** trois données data-derived d'`out/ecf_plan.json` +(byte-gaté par `check_artifacts`) : les **états qui émettent** un e-CF « seulement +réservation et contrat » (README:48) = `{emission_events[].update_value}`, les +**champs Currency de base** « `montant_reservation`, `montant_contrat` » (README:50) += `{emission_events[].base_field}`, et la **FormaPago défaut** « = 3 (Tarjeta) … +via Cardnet (#10) » (README:59) = `forma_pago_defaut` **ancrée sur CLAUDE.md #10** +(Cardnet = carte). Aucun gate ne les comparait à l'artefact : AJOUTER un évènement +(facturer un devis), en RETIRER un, RENOMMER un `base_field` ou CHANGER +`forma_pago_defaut` (Cardnet abandonné → code 2 Transfert) ferait dériver la prose +en silence — l'agent ERPNext Backend câblerait le mauvais évènement / la mauvaise +FormaPago (le risque même que la cross-cohérence veut prévenir), dérive qu'aucune +suite `tests/` (qui teste les FONCTIONS `ncf`/résolution, pas la prose) n'attrape. +On RECOMPUTE chaque ensemble/valeur depuis l'artefact (zéro duplication du contrat +du générateur · #6) et on exige que la prose l'encode exactement (absent ET en trop +via set-diff · accents/casse normalisés). Cohérences croisées en bonus (mordent un +plan **internement incohérent**) : `forma_pago_defaut.code` ∈ catalogue +`formas_pago` · libellé prose ⊆ libellé catalogue du code · ensembles non vides. +7 morsures vérifiées : prose retire `contrat` des émetteurs · prose renomme +`montant_contrat` · prose FormaPago `3→2` (drift artefact) · artefact +`forma_pago_defaut.code 3→2` (prose périmée) · artefact ajoute l'évènement `devis` +(prose sans `devis` — états ET base_field mordus) · FormaPago prose supprimée +(INTROUVABLE) · artefact `code→99` hors catalogue (incohérence interne) ; +restauré = vert. + Même classe enfin sur la **cross-cohérence du barème de commissions** (livrable `crm/commissions`) — le CŒUR du module. La section « Cross-cohérence » de son README énumère **à la main** deux ensembles data-derived + une propriété : les états diff --git a/ci/check_readme_claims.sh b/ci/check_readme_claims.sh index c502f3b..a63996a 100755 --- a/ci/check_readme_claims.sh +++ b/ci/check_readme_claims.sh @@ -1675,6 +1675,112 @@ if encf is not None: re.search(r"`([A-Z])\s*\+\s*tipoeCF\((\d+)\)\s*\+\s*" r"secuencia\((\d+)\)`", ecf_fi), False) +# ============================================================================ +# Fiscal · e-CF DGII (2e surface du MÊME README) · la CROSS-COHÉRENCE +# (« le cœur du livrable ») — ENSEMBLES/valeur data-derived transcrits À LA MAIN +# sans AUCUN gate d'IDENTITÉ. Le bloc e-NCF ci-dessus ne gate QUE le FORMAT de +# l'identifiant ; la section « Cross-cohérence e-CF ↔ workflow ↔ DocType ↔ RBAC » +# ÉNUMÈRE trois données dérivées de out/ecf_plan.json (byte-gaté par +# check_artifacts ⇒ faisant autorité) : +# • README:48 « seulement réservation et contrat » — l'ensemble des états qui +# ÉMETTENT un e-CF = {emission_events[].update_value}. +# • README:50 « (`montant_reservation`, `montant_contrat`) » — l'ensemble des +# champs Currency de base = {emission_events[].base_field}. +# • README:59 « FormaPago défaut = 3 (Tarjeta) … via Cardnet (#10) » — le code + +# libellé de forma_pago_defaut, ANCRÉ sur CLAUDE.md #10 (Cardnet = carte). +# Aucun gate ne comparait ces valeurs à l'artefact : AJOUTER un évènement +# d'émission (facturer un devis) · en RETIRER un · RENOMMER un base_field · +# CHANGER forma_pago_defaut (Cardnet abandonné → code 2 Transfert) ferait dériver +# la prose en silence pendant que l'ecf_plan byte-gaté dit autre chose — l'agent +# ERPNext Backend câblerait le mauvais évènement / la mauvaise FormaPago (le risque +# même que la cross-cohérence veut prévenir) — « vert trompeur » qu'aucune suite +# tests/ (qui teste des FONCTIONS ncf/résolution, pas la prose) n'attrape. Même +# patron EXACT que la cross-cohérence CRM/Commissions (base_field + états soumis). +# On RECOMPUTE chaque ensemble/valeur depuis l'artefact (zéro duplication du +# contrat du générateur #6) et on exige que la prose l'encode EXACTEMENT (absent ET +# en trop via set-diff · accents/casse normalisés). Cohérences croisées en bonus +# (mordent un plan INTERNEMENT incohérent) : forma_pago_defaut.code ∈ catalogue +# formas_pago · libellé prose ⊆ libellé catalogue du code · ensembles NON VIDES. +# Un claim absent échoue AUSSI (traçabilité). +# ============================================================================ +ECF_RD2 = f"{D}/fiscal/ecf_dgii/README.md" +def _ecf_acc(s): + return "".join(c for c in unicodedata.normalize("NFKD", s) + if unicodedata.category(c) != "Mn").lower() +try: + ecfp = load("fiscal/ecf_dgii/out/ecf_plan.json") + ecf_ev = ecfp["emission_events"] + ecf_uv = {_ecf_acc(e["update_value"]) for e in ecf_ev} + ecf_bf = {e["base_field"] for e in ecf_ev} + ecf_fpd = ecfp["forma_pago_defaut"] + ecf_fp_code = str(ecf_fpd["code"]) + ecf_fp_cat = {str(f["code"]): f["label"] for f in ecfp["formas_pago"]} +except (OSError, KeyError, ValueError, TypeError) as e: + bad(f"Fiscal cross-cohérence · artefact ecf_plan illisible/inattendu : {e}"); ecf_ev = None +if ecf_ev is not None: + # bonus : plan INTERNEMENT cohérent (ensembles non vides · défaut ∈ catalogue) + if not ecf_uv or not ecf_bf: + bad("Fiscal cross-cohérence · aucun évènement d'émission (plan vide) → vérifier le plan") + elif ecf_fp_code not in ecf_fp_cat: + bad(f"Fiscal cross-cohérence · forma_pago_defaut.code={ecf_fp_code} ABSENT du " + f"catalogue formas_pago {sorted(ecf_fp_cat)} → plan incohérent") + else: + good(f"Fiscal cross-cohérence · forma_pago_defaut {ecf_fp_code} " + f"({ecf_fp_cat[ecf_fp_code]}) ∈ catalogue formas_pago · émission {sorted(ecf_uv)} non vide") + ecf_rd2 = read_norm(ECF_RD2) + if ecf_rd2 is not None: + # 1) README:48 — états émetteurs « seulement réservation et contrat » + me = re.search(r"seulement\s+([^.]+)\.", ecf_rd2) + if me is None: + bad(f"Fiscal cross-cohérence · {ECF_RD2} — énumération des états émetteurs " + f"INTROUVABLE (attendu {sorted(ecf_uv)})") + else: + es = {_ecf_acc(t.strip()) for t in re.split(r"\bet\b|[,/]", me.group(1)) if t.strip()} + if es == ecf_uv: + good(f"Fiscal cross-cohérence · {ECF_RD2} — états émetteurs {sorted(es)} == " + f"ecf_plan.emission_events[].update_value") + else: + miss = sorted(ecf_uv - es); extra = sorted(es - ecf_uv) + detail = (f" · absents={miss}" if miss else "") + (f" · en trop={extra}" if extra else "") + bad(f"Fiscal cross-cohérence · {ECF_RD2} — énumère {sorted(es)} MAIS plan " + f"{sorted(ecf_uv)}{detail} → régénérer le README") + # 2) README:50 — champs de base « (`montant_reservation`, `montant_contrat`) » + mf = re.search(r"Currency réel\*\* du DocType Dossier Vente\s*\(([^)]*)\)", ecf_rd2) + if mf is None: + bad(f"Fiscal cross-cohérence · {ECF_RD2} — énumération des champs de base " + f"INTROUVABLE (attendu {sorted(ecf_bf)})") + else: + bs = set(re.findall(r"`([a-z_]+)`", mf.group(1))) + if bs == ecf_bf: + good(f"Fiscal cross-cohérence · {ECF_RD2} — champs de base {sorted(bs)} == " + f"ecf_plan.emission_events[].base_field") + else: + miss = sorted(ecf_bf - bs); extra = sorted(bs - ecf_bf) + detail = (f" · absents={miss}" if miss else "") + (f" · en trop={extra}" if extra else "") + bad(f"Fiscal cross-cohérence · {ECF_RD2} — énumère {sorted(bs)} MAIS plan " + f"{sorted(ecf_bf)}{detail} → régénérer le README") + # 3) README:59 — FormaPago défaut « = 3 (Tarjeta) … via Cardnet (#10) » + mp = re.search(r"FormaPago défaut\s*=\s*(\d+)\s*\(([^)]*)\)", ecf_rd2) + if mp is None: + bad(f"Fiscal cross-cohérence · {ECF_RD2} — FormaPago défaut INTROUVABLE " + f"(attendu code {ecf_fp_code} · {ecf_fp_cat.get(ecf_fp_code,'?')})") + else: + got_code = mp.group(1); got_lbl = _ecf_acc(mp.group(2).strip()) + cat_lbl = _ecf_acc(ecf_fp_cat.get(ecf_fp_code, "")) + has_cardnet = "cardnet" in _ecf_acc(ecf_rd2[mp.start():mp.start() + 160]) + if got_code == ecf_fp_code and got_lbl and got_lbl in cat_lbl and has_cardnet: + good(f"Fiscal cross-cohérence · {ECF_RD2} — FormaPago défaut {got_code} " + f"({mp.group(2).strip()}) == forma_pago_defaut · ancré Cardnet (#10)") + elif got_code != ecf_fp_code: + bad(f"Fiscal cross-cohérence · {ECF_RD2} — FormaPago défaut = {got_code} MAIS " + f"forma_pago_defaut.code = {ecf_fp_code} → régénérer le README") + elif not (got_lbl and got_lbl in cat_lbl): + bad(f"Fiscal cross-cohérence · {ECF_RD2} — libellé FormaPago {mp.group(2).strip()!r} " + f"≠ catalogue code {ecf_fp_code} ({ecf_fp_cat.get(ecf_fp_code)!r}) → régénérer") + else: + bad(f"Fiscal cross-cohérence · {ECF_RD2} — FormaPago défaut sans ancrage " + f"Cardnet (#10) à proximité → régénérer le README") + # ============================================================================ # CRM/Commissions · barème vendeurs — la CROSS-COHÉRENCE (« cœur du livrable ») # transcrite À LA MAIN dans le README sans AUCUN gate d'IDENTITÉ.