c06e15c058
Audit de second niveau : lit les hand-off out/ des livrables (workflow vente, Dossier Vente, commissions, e-CF DGII, CONFOTUR) et vérifie 17 contrôles en 5 dimensions (D1 Traçabilité/ISA 500 · D2 AML-UAF/Ley 155-17 · D3 Fiscal e-CF/Ley 32-23 · D4 Intégrité/IFRS · D5 Gouvernance-SoD/ISA 315). Anti-invention #6 : paramètre réglementaire non confirmé → A_CONFIRMER (open item assigné au métier), jamais fabriqué. Verdict PASS_WITH_OPEN_ITEMS (13 PASS, 0 FAIL, 4 à confirmer). Réutilise validateur Publiciste + RoleResolver CRM + roles_targeting CONFOTUR (zéro duplication). 37 tests · 15 invariants · build déterministe · régression 341 tests verts. Job CI qa-audit-5d-tests + gate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
54 lines
2.0 KiB
Python
54 lines
2.0 KiB
Python
"""Chargement des livrables audités (sortie `out/` des modules voisins).
|
|
|
|
L'audit 5D est un audit de SECOND NIVEAU : sa matière première est le hand-off
|
|
JSON déjà commité par chaque générateur. On ne relance pas les générateurs — on
|
|
lit leur produit tel qu'il sera importé côté VPS. Chaque artefact absent lève une
|
|
erreur explicite (un audit sur un livrable manquant serait mensonger).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
from typing import Any
|
|
|
|
_HERE = os.path.dirname(os.path.abspath(__file__))
|
|
_DELIVERABLES = os.path.normpath(os.path.join(_HERE, "..", "..", ".."))
|
|
|
|
# id logique → chemin relatif au dossier 05_deliverables_mvp
|
|
ARTIFACT_PATHS: dict[str, str] = {
|
|
"workflow": os.path.join("crm", "workflow_vente", "out", "workflow.json"),
|
|
"dossier": os.path.join("crm", "dossier_vente", "out",
|
|
"doctype_oto_dossier_vente.json"),
|
|
"commissions": os.path.join("crm", "commissions", "out",
|
|
"commission_plan.json"),
|
|
"ecf": os.path.join("fiscal", "ecf_dgii", "out", "ecf_plan.json"),
|
|
"confotur": os.path.join("legal", "confotur", "out",
|
|
"doctype_confotur_application.json"),
|
|
}
|
|
|
|
|
|
def _load(rel: str) -> Any:
|
|
path = os.path.join(_DELIVERABLES, rel)
|
|
if not os.path.exists(path):
|
|
raise FileNotFoundError(
|
|
f"Artefact audité introuvable : {rel} — le générateur amont doit "
|
|
f"avoir écrit son `out/` avant l'audit 5D (aucun audit à vide)."
|
|
)
|
|
with open(path, encoding="utf-8") as fh:
|
|
return json.load(fh)
|
|
|
|
|
|
def load_artifacts() -> dict[str, Any]:
|
|
"""Charge les 5 livrables. Le workflow est une liste ; on expose l'objet."""
|
|
raw = {aid: _load(rel) for aid, rel in ARTIFACT_PATHS.items()}
|
|
wf = raw["workflow"]
|
|
if not isinstance(wf, list) or not wf:
|
|
raise ValueError("workflow.json attendu = liste non vide de Workflow.")
|
|
raw["workflow"] = wf[0]
|
|
return raw
|
|
|
|
|
|
def known_ids() -> frozenset[str]:
|
|
return frozenset(ARTIFACT_PATHS)
|