[DTP-Worker 20260801_203424] Sprint 8 · buffer · Audit 5D (4e surface du même README) : gate d'IDENTITÉ des LIVRABLES AUDITÉS — la liste des 5 hand-off out/*.json que l'audit de second niveau LIT (README qa/audit_5d §« Livrables audités »), énumérée à la main hors de tout gate ; recomputée par ENSEMBLE de MANIFEST.audited_artifacts (byte-gaté · = sorted(ARTIFACT_PATHS.values())), ordre README ≠ ordre MANIFEST trié ⇒ set-diff absent/fantôme ; cross-anchors : chaque chemin résout un fichier commité (a) + |audités| == |alias distincts des controls[].artifacts du spec| (b) ; 6 morsures vérifiées (4 README · 2 source), 7 gates re-verts

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Code DTP Worker
2026-08-01 20:46:10 +00:00
parent 17c3d2b104
commit f91a22f458
3 changed files with 192 additions and 1 deletions
+93
View File
@@ -2317,6 +2317,99 @@ if a5d_exp is not None:
good(f"Audit 5D · IDENTITÉ des 5 dimensions {a5d_order} "
f"(id→titre→reference) == audit_report.dimensions")
# ============================================================================
# Audit 5D (4e surface du MÊME README) · IDENTITÉ des LIVRABLES AUDITÉS — la liste
# des hand-off `out/*.json` que l'audit de second niveau LIT comme matière première,
# énumérée À LA MAIN dans la section « ## Livrables audités » — même classe de
# « vert trompeur ».
# ----------------------------------------------------------------------------
# Les 3 blocs audit_5d ci-dessus gatent la SYNTHÈSE (contrôles/dimensions/verdict),
# l'ÉNUMÉRATION des open items et l'IDENTITÉ des 5 dimensions — AUCUN ne touche la
# liste des ARTEFACTS SOURCES. Cette liste vit dans MANIFEST.audited_artifacts
# (byte-gaté par check_artifacts ⇒ faisant autorité : le générateur la recompute à
# chaque build via `sorted(ARTIFACT_PATHS.values())`, le registre des hand-off amont
# que `load_artifacts()` charge réellement). Le README (05_deliverables_mvp/qa/
# audit_5d/README.md, section « ## Livrables audités (source = leur `out/`) »)
# réénumère ces 5 chemins À LA MAIN — dans un ORDRE DIFFÉRENT du MANIFEST (workflow
# 2e ici, 3e au MANIFEST, trié) ⇒ comparaison par ENSEMBLE. Piège : brancher un
# nouveau livrable amont sur l'audit (nouvelle entrée ARTIFACT_PATHS → nouveau chemin
# dans audited_artifacts) ou en retirer un laisse la section README PÉRIMÉE en
# silence pendant que l'audit lit un autre jeu de fichiers — le lecteur/banquier
# croirait l'audit couvrir un périmètre qu'il ne couvre plus (l'invention #6
# interdit), qu'aucune suite tests/ (qui teste des FONCTIONS de contrôle, pas la
# prose) n'attrape. On RECOMPUTE l'ensemble depuis MANIFEST.audited_artifacts (zéro
# duplication du registre) et on exige que les puces README l'énumèrent EXACTEMENT
# (absent ET fantôme mordus). Cohérences croisées bonus (mordent un MANIFEST/spec
# INTERNEMENT incohérent) : (a) chaque chemin audité RÉSOUT un fichier commité sur
# disque (le `_load` de l'auditeur lève sinon — audit à vide impossible) ; (b) le
# NOMBRE de livrables audités == le nombre d'ALIAS distincts d'artefacts référencés
# par les contrôles du spec (audit_spec.json · union des `controls[].artifacts`) : un
# artefact chargé mais jamais contrôlé, ou un alias contrôlé sans chemin de
# chargement (KeyError runtime), désynchronise les deux. Section absente ⇒ ROUGE
# (traçabilité #6). Direction README/MANIFEST → registre/spec.
# ============================================================================
import os
try:
a5a_exp = load("qa/audit_5d/out/MANIFEST.json")["audited_artifacts"]
a5a_ctrls = load("qa/audit_5d/audit_spec.json")["controls"]
except (OSError, KeyError, ValueError) as e:
bad(f"Audit 5D · livrables audités illisibles/inattendus : {e}"); a5a_exp = None
if a5a_exp is not None:
a5a_exp_set = set(a5a_exp)
# tri/dédup : audited_artifacts = sorted(dict.values()) — mord un MANIFEST malformé
if a5a_exp != sorted(a5a_exp_set) or len(a5a_exp) != len(a5a_exp_set):
bad(f"Audit 5D · MANIFEST.audited_artifacts non trié/dédupliqué : {a5a_exp}")
# (a) chaque chemin audité résout un fichier commité (audit à vide impossible)
a5a_missing = [p for p in a5a_exp if not os.path.exists(f"{D}/{p}")]
if a5a_missing:
bad(f"Audit 5D · livrables audités INTROUVABLES sur disque {a5a_missing} → "
f"l'auditeur lèverait FileNotFoundError (aucun audit à vide)")
else:
good(f"Audit 5D · les {len(a5a_exp)} livrables audités résolvent tous un "
f"fichier commité")
# (b) |audited| == |alias distincts référencés par les contrôles du spec|
a5a_alias = set()
for ctl in a5a_ctrls:
a5a_alias |= set(ctl.get("artifacts", []))
if len(a5a_alias) == len(a5a_exp_set):
good(f"Audit 5D · {len(a5a_exp_set)} livrables audités == {len(a5a_alias)} "
f"alias d'artefacts distincts contrôlés par le spec (aucun mort/orphelin)")
else:
bad(f"Audit 5D · désynchro registre/spec : {len(a5a_exp_set)} livrables "
f"audités MAIS {len(a5a_alias)} alias distincts contrôlés "
f"({sorted(a5a_alias)}) → artefact chargé jamais contrôlé, ou alias sans "
f"chemin de chargement")
# README — puces de la section « ## Livrables audités »
try:
a5a_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}"); a5a_lines = None
if a5a_lines is not None:
a5a_got, a5a_in_sec = set(), False
a5a_bullet = re.compile(r"^-\s+`([^`]+)`\s*$")
for ln in a5a_lines:
if re.match(r"^##\s+Livrables audités\b", ln):
a5a_in_sec = True; continue
if a5a_in_sec and ln.startswith("## "):
break
if a5a_in_sec:
mb = a5a_bullet.match(ln)
if mb:
a5a_got.add(mb.group(1))
if not a5a_got:
bad(f"Audit 5D · section « ## Livrables audités » INTROUVABLE/vide dans "
f"{D}/qa/audit_5d/README.md (attendu {sorted(a5a_exp_set)})")
elif a5a_got == a5a_exp_set:
good(f"Audit 5D · livrables audités — les {len(a5a_got)} puces README == "
f"MANIFEST.audited_artifacts (aucun fantôme/manquant)")
else:
miss = sorted(a5a_exp_set - a5a_got); extra = sorted(a5a_got - a5a_exp_set)
detail = (f" · absents={miss}" if miss else "") + \
(f" · en trop (fantôme)={extra}" if extra else "")
bad(f"Audit 5D · livrables audités — README énumère {sorted(a5a_got)} MAIS "
f"MANIFEST dit {sorted(a5a_exp_set)}{detail} → régénérer le README")
# ============================================================================
# DevOps · run-book de déploiement VPS (phases ordonnées / confirmations
# préalables) — même classe de « vert trompeur », transcrite À LA MAIN dans