[DTP-Worker] Sprint 8 · buffer · QA/audit_5d : gate d'IDENTITÉ des 5 DIMENSIONS du cadre

3e surface du MÊME README `qa/audit_5d` : la table « ## Les 5 dimensions »
(README:15-19) transcrit par ligne le triplet (id · titre · reference) de chaque
dimension. Jusqu'ici gatée seulement sur son COMPTE agrégé (« 5 dimensions ») — un
WILDCARD aveugle à QUELLE dimension. Une norme de référence permutée (D3 fiscal
`Ley 32-23`, D1 probatoire `ISA 500`) est une affirmation de conformité fausse
(l'invention même que #6 interdit) sur l'audit de second niveau que le banquier lit.

Gate ajouté (`ci/check_readme_claims.sh`) : liste ORDONNÉE {id:(titre,reference)}
recomputée de `qa/audit_5d/out/audit_report.json[dimensions]` (byte-gaté) — identité
par ensemble d'id (absent/en trop), ordre préservé (D1→D5), (titre,reference) par
ligne. Cohérence croisée bonus : rapport == audit_spec.json[dimensions] (l'INPUT dont
il dérive). Table disparue = échec (traçabilité #6).

8 morsures vérifiées (README rename titre/permute norme ISA/Ley/supprime ligne/
réordonne · artefact & spec renommés · table supprimée) ; restauré green · 7 gates
re-verts. Édition hors `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact.
ci/README.md (recap + paragraphe détaillé) mis à jour.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Code DTP Worker
2026-08-01 16:41:34 +00:00
parent 85a9ba7fca
commit a52daa056f
3 changed files with 157 additions and 1 deletions
+76
View File
@@ -2019,6 +2019,82 @@ if a5_oi_exp is not None:
good(f"Audit 5D · énumération open items {sorted(a5_exp_set)} "
f"(control→dimension→propriétaire) == audit_report.open_items")
# ============================================================================
# Audit 5D (3e surface du MÊME README) · IDENTITÉ des 5 DIMENSIONS — le libellé
# (`titre`) ET la NORME de référence (`reference`) de chaque dimension du cadre —
# même classe de « vert trompeur », transcrite À LA MAIN dans un tableau.
# ----------------------------------------------------------------------------
# Le 1er bloc audit_5d gate le COMPTE de dimensions (« 5 dimensions (5D) ») — un
# WILDCARD AVEUGLE à QUELLE dimension : renommer « Traçabilité & anti-invention »
# ou permuter une norme de référence (D3 fiscal citant la mauvaise Ley, D1
# probatoire citant `ISA 501` au lieu d'`ISA 500`) laisse le compte à 5 pendant que
# l'artefact dit autre chose. La table « ## Les 5 dimensions » du README
# (05_deliverables_mvp/qa/audit_5d/README.md) énumère PAR LIGNE le triplet
# (`id`, `titre`, `reference`) alors que audit_report.dimensions[] (byte-gaté par
# check_artifacts ⇒ faisant autorité, chaque dimension recomposée du catalogue de
# contrôles à chaque build) fait foi. C'est l'audit de SECOND NIVEAU que
# l'auditeur/banquier lit : une norme de référence périmée (D2 AML citant une
# mauvaise Ley 155-17, D3 fiscal la mauvaise Ley 32-23) est une AFFIRMATION de
# conformité fausse — exactement l'invention que #6 interdit, qu'aucune suite
# tests/ (qui teste des FONCTIONS de contrôle, pas la prose) n'attrape. On RECOMPUTE
# la liste ORDONNÉE {id:(titre, reference)} depuis audit_report.dimensions[] (zéro
# duplication du modèle de l'auditeur · #6) et on exige que la table README
# l'énumère EXACTEMENT : identité par ENSEMBLE d'`id` (absent ET en trop mordus),
# ORDRE préservé (D1→D5), puis (titre, reference) par LIGNE. Même patron que
# l'énumération des open items ci-dessus. Cohérence croisée en bonus : les
# dimensions du rapport == celles de audit_spec.json (l'INPUT dont le rapport
# dérive) — mord un builder qui renommerait/permuterait une dimension en aval du
# spec. Un claim absent (table disparue) échoue AUSSI (traçabilité #6).
# ============================================================================
try:
a5d_dim = load("qa/audit_5d/out/audit_report.json")["dimensions"]
a5d_exp = {d["id"]: (d["titre"], d["reference"]) for d in a5d_dim}
a5d_order = [d["id"] for d in a5d_dim]
a5d_spec = load("qa/audit_5d/audit_spec.json")["dimensions"]
a5d_spec_map = {d["id"]: (d["titre"], d["reference"]) for d in a5d_spec}
if a5d_spec_map != a5d_exp:
bad(f"Audit 5D · incohérence artefacts : audit_spec dimensions {sorted(a5d_spec_map)} "
f"divergent du rapport {sorted(a5d_exp)} (titre/reference) → le rapport doit "
f"dériver du spec")
except (OSError, KeyError, ValueError) as e:
bad(f"Audit 5D · dimensions illisibles/inattendues : {e}"); a5d_exp = None
if a5d_exp is not None:
try:
a5d_lines = open(f"{D}/qa/audit_5d/README.md",
encoding="utf-8").read().splitlines()
except OSError as e:
bad(f"Audit 5D · README illisible : {e}"); a5d_lines = None
if a5d_lines is not None:
# ligne de table = « | **D1** | <titre> | <reference> | » (gras + PAS de
# sous-point ⇒ distingue des lignes open items « | D1.1 · … | »)
a5d_row_re = re.compile(
r"^\|\s*\*\*(D\d+)\*\*\s*\|\s*([^|]+?)\s*\|\s*([^|]+?)\s*\|\s*$")
a5d_seen, a5d_seen_order = {}, []
for ln in a5d_lines:
m = a5d_row_re.match(ln)
if m:
a5d_seen[m.group(1)] = (m.group(2).strip(), m.group(3).strip())
a5d_seen_order.append(m.group(1))
a5d_exp_ids, a5d_got_ids = set(a5d_exp), set(a5d_seen)
if a5d_got_ids != a5d_exp_ids:
bad(f"Audit 5D · IDENTITÉ des dimensions — table README énumère "
f"{sorted(a5d_got_ids)} MAIS audit_report dit {sorted(a5d_exp_ids)} "
f"→ régénérer le README")
elif a5d_seen_order != a5d_order:
bad(f"Audit 5D · IDENTITÉ des dimensions — ORDRE README {a5d_seen_order} "
f"MAIS audit_report dit {a5d_order} → régénérer le README")
else:
a5d_mism = [i for i in a5d_order if a5d_seen[i] != a5d_exp[i]]
if a5d_mism:
for i in a5d_mism:
bad(f"Audit 5D · dimension {i} — README dit "
f"(titre={a5d_seen[i][0]!r}, reference={a5d_seen[i][1]!r}) MAIS "
f"audit_report dit (titre={a5d_exp[i][0]!r}, "
f"reference={a5d_exp[i][1]!r})")
else:
good(f"Audit 5D · IDENTITÉ des 5 dimensions {a5d_order} "
f"(id→titre→reference) == audit_report.dimensions")
# ============================================================================
# DevOps · run-book de déploiement VPS (phases ordonnées / confirmations
# préalables) — même classe de « vert trompeur », transcrite À LA MAIN dans