[DTP-Worker] Sprint 8 · buffer · Fiscal/e-CF DGII : l'IDENTITÉ du RÔLE émetteur e-CF — le SEUL rôle habilité à ÉMETTRE un comprobante fiscal électronique, nommé en prose par le bullet role_id du README (fiscal/ecf_dgii/README.md:51-54) via TROIS attributs data-derived (role_id compta-fiscaliste-ecf · erpnext_role_name OTO Compta Fiscaliste eCF · portail compta) — était HORS de tout gate d'IDENTITÉ.
Le bloc e-NCF gate le FORMAT de l'identifiant ; le bloc cross-cohérence (2e surface) gate états émetteurs/champs de base/FormaPago ; mais AUCUN ne comparait l'IDENTITÉ de ce rôle à ses DEUX sources faisant autorité : (a) out/ecf_plan.json (byte-gaté) — chaque emission_events[] porte role_id + erpnext_role_name ; (b) rbac_50_roles.json (contrat) — le rôle porte portail:"compta". Le `portail` n'est PAS dans l'artefact ; il n'est vérifiable QUE contre le contrat. Même classe d'IDENTITÉ que la Fiche Mobile / les Fiches Faisabilité ou les gardes CRM/workflow_vente, appliquée à une 3e surface distincte du MÊME README fiscal. Piège : la seule vérification existante (tests/) teste des FONCTIONS (ncf/résolution), jamais la prose. RÉAFFECTER l'émission à un autre rôle, le RENOMMER, ou DÉPLACER le rôle hors du portail compta (e-CF émis HORS Compta — séparation cassée) laisse la prose périmée pendant que l'artefact/contrat disent autre chose → l'agent ERPNext Backend câblerait le mauvais rôle émetteur. Gate ajouté (ci/check_readme_claims.sh, bloc « Fiscal rôle émetteur ») : role_id/erpnext_role_name recomputés de emission_events + portail/nom Frappe du contrat, prose EXACTE ; cohérences croisées : tous les emission_events pointent UN SEUL rôle (fonction · non vide) · role_id unique au contrat · nom artefact == contrat (zéro-dup) · portail == compta. Un claim absent échoue AUSSI. 6 morsures vérifiées (README renomme role_id/nom/portail · artefact réaffecte les 2 events à ventes-directeur (5 mords cascade) · contrat déplace le rôle vers ventes · artefact diverge du contrat sur le nom), restauré vert, 7 gates re-verts. ci/README.md mis à jour. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -358,3 +358,59 @@ byte-restauré (`git checkout --`, **JAMAIS** `git clean`, interdit absolu) ·
|
||||
édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact ; le déploiement
|
||||
réel du site `vente.otov7.com` reste côté VPS).
|
||||
- **Auto-score 4Big** : 96/100.
|
||||
|
||||
## Session `20260801_030214` · Buffer S8 · Domaine Fiscal/e-CF DGII : l'**IDENTITÉ du RÔLE émetteur** e-CF — le SEUL rôle habilité à ÉMETTRE un comprobante fiscal électronique — nommé **en prose** par le bullet `role_id` du README (`fiscal/ecf_dgii/README.md:51-54`) via TROIS attributs data-derived (`role_id` `compta-fiscaliste-ecf` · `erpnext_role_name` `OTO Compta Fiscaliste eCF` · `portail` `compta`) était **HORS de tout gate d'IDENTITÉ**. Le bloc **e-NCF** gate le FORMAT de l'identifiant ; le bloc **cross-cohérence** (2e surface, session précédente) gate états émetteurs/champs de base/FormaPago ; mais AUCUN ne comparait l'IDENTITÉ de ce rôle à ses deux sources faisant autorité.
|
||||
|
||||
**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 classe d'IDENTITÉ**
|
||||
que la Fiche Mobile / les Fiches Faisabilité (`agent-fiche-role-attrs-ungated`) ou les
|
||||
gardes CRM/`workflow_vente` (rôle→transition) — appliquée à une **3e surface distincte
|
||||
du MÊME README `fiscal/ecf_dgii`** : le rôle émetteur nommé dans la section
|
||||
« Cross-cohérence », jamais gaté hors le FORMAT e-NCF et les ensembles états/champs.
|
||||
|
||||
**Dérive silencieuse fermée** :
|
||||
- `05_deliverables_mvp/fiscal/ecf_dgii/README.md:53-54` — bullet `role_id` :
|
||||
« **et** appartenir au portail `compta` — le rôle dédié `compta-fiscaliste-ecf`
|
||||
(_OTO Compta Fiscaliste eCF_) ».
|
||||
- Sources faisant autorité (double) : (a) `out/ecf_plan.json` (byte-gaté par
|
||||
`check_artifacts`) — CHAQUE `emission_events[]` porte `role_id` +
|
||||
`erpnext_role_name` ; (b) `rbac/rbac_50_roles.json` (contrat schéma-validé ·
|
||||
les 3 MANIFEST RBAC en dérivent, byte-gatés) — le rôle d'`id`
|
||||
`compta-fiscaliste-ecf` porte `portail: "compta"`. Le `portail` n'est PAS dans
|
||||
l'artefact ; il n'est vérifiable QUE contre le contrat.
|
||||
- Piège : la seule vérification existante (`tests/`) teste des **FONCTIONS**
|
||||
(`ncf`/résolution RBAC), jamais la PROSE. RÉAFFECTER l'émission à un autre rôle
|
||||
(dans le plan), le RENOMMER, ou DÉPLACER `compta-fiscaliste-ecf` du portail
|
||||
`compta` vers un autre portail (dans le contrat : l'e-CF serait émis **HORS
|
||||
Compta** — la séparation même que la cross-cohérence pose) laisse la prose périmée
|
||||
EN SILENCE pendant que l'artefact/le contrat disent autre chose → l'agent ERPNext
|
||||
Backend câblerait le mauvais rôle émetteur (le risque même que #6 interdit).
|
||||
- **État courant** : **aucun attribut périmé** — les 3 recoupent artefact+contrat
|
||||
exactement (anti-invention #6, rien à réécrire). Le défaut est la **surface
|
||||
ungated**.
|
||||
|
||||
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « Fiscal rôle émetteur »
|
||||
après le bloc FormaPago) : (1) **par attribut** — `role_id`/`erpnext_role_name`
|
||||
recomputés de `emission_events` + `portail`/nom Frappe recomputés du contrat, prose
|
||||
EXACTE exigée (regex par claim) ; (2) **cohérences croisées bonus** (mordent un
|
||||
plan/contrat INTERNEMENT incohérent) : tous les `emission_events` pointent UN SEUL
|
||||
rôle (fonction · non vide) · ce `role_id` existe EXACTEMENT une fois au contrat · le
|
||||
nom résolu dans l'artefact == le contrat (zéro-duplication) · `portail == compta`
|
||||
(séparation des pouvoirs). Un claim absent échoue AUSSI (traçabilité #6).
|
||||
|
||||
**6 morsures vérifiées** : README renomme le `role_id`
|
||||
(`compta-fiscaliste-ecf→…-xxx`, prose périmée) · README renomme le nom Frappe · README
|
||||
déplace le portail (`compta→ventes`) · **artefact** réaffecte les DEUX
|
||||
`emission_events` à `ventes-directeur` (5 mords en cascade : nom≠contrat + portail
|
||||
HORS Compta + les 3 claims de prose) · **contrat** déplace le rôle vers `ventes`
|
||||
(portail HORS Compta + prose portail périmée) · **artefact** diverge du contrat sur
|
||||
le nom (`OTO Compta X`, zéro-dup cassé) ; restauré = green : 3 claims == artefact/
|
||||
contrat · exit 0. Working tree byte-restauré (`git checkout --`, **JAMAIS**
|
||||
`git clean`, interdit absolu) · **7 gates re-verts**.
|
||||
|
||||
- `ci/README.md` (table récap du pipeline + paragraphe détaillé « identité du RÔLE
|
||||
émetteur e-CF, 3e surface du MÊME README `fiscal/ecf_dgii` ») mis à jour.
|
||||
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ;
|
||||
édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact ; le seed réel
|
||||
du rôle + le câblage Compupar restent côté VPS).
|
||||
- **Auto-score 4Big** : 96/100.
|
||||
|
||||
+25
-1
File diff suppressed because one or more lines are too long
@@ -2062,6 +2062,78 @@ if ecf_ev is not None:
|
||||
bad(f"Fiscal cross-cohérence · {ECF_RD2} — FormaPago défaut sans ancrage "
|
||||
f"Cardnet (#10) à proximité → régénérer le README")
|
||||
|
||||
# ============================================================================
|
||||
# Fiscal · e-CF DGII (3e surface du MÊME README) · l'IDENTITÉ du RÔLE émetteur —
|
||||
# le seul rôle habilité à ÉMETTRE un e-CF, transcrit À LA MAIN sans gate d'IDENTITÉ.
|
||||
# ----------------------------------------------------------------------------
|
||||
# La section « Cross-cohérence » du README (bullet `role_id`, README:51-54) NOMME
|
||||
# le rôle émetteur par TROIS attributs data-derived :
|
||||
# • README:54 « le rôle dédié `compta-fiscaliste-ecf` » — le `role_id` que TOUS
|
||||
# les emission_events d'ecf_plan.json (byte-gaté) référencent.
|
||||
# • README:54 « (_OTO Compta Fiscaliste eCF_) » — son `erpnext_role_name`.
|
||||
# • README:53 « appartenir au portail `compta` » — le `portail` de CE rôle dans
|
||||
# le contrat rbac_50_roles.json (schéma-validé · byte-gaté par les 3 MANIFEST).
|
||||
# Le bloc e-NCF gate le FORMAT de l'identifiant, le bloc cross-cohérence gate les
|
||||
# ÉTATS émetteurs / champs de base / FormaPago — mais AUCUN ne compare l'IDENTITÉ
|
||||
# de ce rôle à ses deux sources. RÉAFFECTER l'émission à un autre rôle (dans le
|
||||
# plan), le RENOMMER, ou DÉPLACER le rôle `compta-fiscaliste-ecf` du portail
|
||||
# `compta` vers un autre portail (dans le contrat : l'e-CF serait alors émis HORS
|
||||
# Compta — la séparation même que la cross-cohérence pose) laisse la prose périmée
|
||||
# en silence pendant que l'artefact/le contrat disent autre chose ⇒ l'agent ERPNext
|
||||
# Backend câblerait le mauvais rôle émetteur (le risque même que #6 interdit).
|
||||
# Aucune suite tests/ (qui teste des FONCTIONS ncf/résolution, pas la prose) ne
|
||||
# l'attrape — même patron d'IDENTITÉ que la Fiche Mobile ou les gardes CRM. On
|
||||
# RECOMPUTE `role_id`/`erpnext_role_name` de l'artefact et `portail`/nom Frappe du
|
||||
# contrat (zéro duplication) et on exige la prose EXACTE. Cohérences croisées bonus
|
||||
# (mordent un plan/contrat INTERNEMENT incohérent) : tous les emission_events
|
||||
# pointent UN SEUL rôle (fonction · non vide) · ce role_id existe UNE fois au
|
||||
# contrat · le nom résolu dans l'artefact == le contrat (zéro-dup) · portail ==
|
||||
# `compta`. Un claim absent échoue AUSSI (traçabilité #6).
|
||||
# ============================================================================
|
||||
ECF_RD3 = f"{D}/fiscal/ecf_dgii/README.md"
|
||||
try:
|
||||
ecfp3 = load("fiscal/ecf_dgii/out/ecf_plan.json")
|
||||
ecf_ev3 = ecfp3["emission_events"]
|
||||
ecf_rids = {e["role_id"] for e in ecf_ev3}
|
||||
ecf_rnames = {e["erpnext_role_name"] for e in ecf_ev3}
|
||||
ecf_contract = load("rbac/rbac_50_roles.json")["roles"]
|
||||
except (OSError, KeyError, ValueError, TypeError) as e:
|
||||
bad(f"Fiscal rôle émetteur · ecf_plan / rbac_50_roles illisible/inattendu : {e}"); ecf_ev3 = None
|
||||
if ecf_ev3 is not None:
|
||||
ecf_rd3 = read_norm(ECF_RD3)
|
||||
# bonus : tous les emission_events pointent UN SEUL rôle (fonction · non vide)
|
||||
if len(ecf_rids) != 1 or len(ecf_rnames) != 1:
|
||||
bad(f"Fiscal rôle émetteur · emission_events référencent {sorted(ecf_rids)} role_id / "
|
||||
f"{sorted(ecf_rnames)} noms (attendu 1 rôle émetteur unique) → plan incohérent")
|
||||
elif ecf_rd3 is not None:
|
||||
ecf_rid = next(iter(ecf_rids)); ecf_art_name = next(iter(ecf_rnames))
|
||||
ecf_match = [r for r in ecf_contract if r.get("id") == ecf_rid]
|
||||
if len(ecf_match) != 1:
|
||||
bad(f"Fiscal rôle émetteur · le contrat porte {len(ecf_match)} rôle(s) `{ecf_rid}` "
|
||||
f"(attendu exactement 1) → rôle absent/dupliqué du contrat rbac_50_roles.json")
|
||||
else:
|
||||
rc = ecf_match[0]
|
||||
ecf_ct_name = rc.get("erpnext_role_name"); ecf_portail = rc.get("portail")
|
||||
# bonus zéro-duplication : le nom résolu dans l'artefact == le contrat
|
||||
if ecf_ct_name != ecf_art_name:
|
||||
bad(f"Fiscal rôle émetteur · ecf_plan dit erpnext_role_name={ecf_art_name!r} MAIS "
|
||||
f"contrat dit {ecf_ct_name!r} pour `{ecf_rid}` → régénérer l'artefact")
|
||||
# bonus séparation des pouvoirs : ce rôle est bien du portail compta (README:53)
|
||||
if ecf_portail != "compta":
|
||||
bad(f"Fiscal rôle émetteur · le rôle `{ecf_rid}` est du portail {ecf_portail!r} "
|
||||
f"≠ `compta` au contrat → l'e-CF serait émis HORS Compta (séparation cassée)")
|
||||
# prose : 3 claims data-derived recomputés (role_id · nom Frappe · portail)
|
||||
for label, pat, exp in [
|
||||
("role_id", rf"rôle dédié `{re.escape(ecf_rid)}`", ecf_rid),
|
||||
("nom d'affichage ERPNext", rf"\(_{re.escape(ecf_ct_name)}_\)", ecf_ct_name),
|
||||
("portail", rf"portail `{re.escape(ecf_portail)}`", ecf_portail),
|
||||
]:
|
||||
if re.search(pat, ecf_rd3):
|
||||
good(f"Fiscal rôle émetteur · {label} « {exp} » == ecf_plan/contrat rbac")
|
||||
else:
|
||||
bad(f"Fiscal rôle émetteur · {ECF_RD3} — {label} : la prose ne cite pas "
|
||||
f"« {exp} » (recomputé de emission_events + rbac_50_roles) → régénérer")
|
||||
|
||||
# ============================================================================
|
||||
# CRM/Commissions · barème vendeurs — la CROSS-COHÉRENCE (« cœur du livrable »)
|
||||
# transcrite À LA MAIN dans le README sans AUCUN gate d'IDENTITÉ.
|
||||
|
||||
Reference in New Issue
Block a user