[DTP-Worker] Sprint 8 · buffer L75 · Domaine DevOps : le run-book de déploiement VPS (devops/deploy_runbook) affichait « 7 phases » (fiche agent devops, ×2 transcriptions) et « 7 confirmations préalables » (README du module) — deux nombres data-derived transcrits À LA MAIN dans DEUX docs sans AUCUN gate, alors que devops/deploy_runbook/out/MANIFEST.json (byte-gaté par check_artifacts, counts.phases=len(graph.phase_order) · counts.confirmations=len(confirmations_ouvertes), recomptés à chaque build) fait autorité. Ajouter une phase au graphe OU une confirmation au catalogue ferait dériver les docs en silence — piège AVEUGLE : phases ET confirmations valent tous deux 7 aujourd'hui, un gate qui n'en recompterait qu'UN serait aveugle à la dérive de l'autre → nouveau bloc dans check_readme_claims recomputant CHAQUE nombre depuis SON PROPRE champ du MANIFEST et exigeant l'égalité avec les docs. Le « 22 » (suites gated de la fiche) volontairement écarté (sémantique suites CI ≠ counts.modules qui s'auto-exclut). Bonus : l'énumération snake_case des confirmations du README doit être l'ensemble EXACT de confirmations_ouvertes (mord l'échange 1↔1 d'un nom, aveugle au compte). Cohérences croisées counts.phases==|phase_order| · counts.confirmations==|confirmations_ouvertes| (5 morsures vérifiées : dérive phases 1re transcription · dérive 2e transcription captée là où la 1re resterait 7 · dérive confirmations count · swap d'un nom captée là où le count reste 7 · claim confirmations disparu = red)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -883,6 +883,100 @@ if a5_ctl is not None:
|
||||
bad(f"Audit 5D · {A5D_FI} — dit {'/'.join(m.groups())} MAIS artefact dit "
|
||||
f"{a5_ctl}/{a5_dim} ({a5_src}) → régénérer la fiche")
|
||||
|
||||
# ============================================================================
|
||||
# DevOps · run-book de déploiement VPS (phases ordonnées / confirmations
|
||||
# préalables) — même classe de « vert trompeur », transcrite À LA MAIN dans
|
||||
# DEUX docs, sans AUCUN gate.
|
||||
# ----------------------------------------------------------------------------
|
||||
# Le générateur deploy_runbook émet out/MANIFEST.json (byte-gaté par
|
||||
# check_artifacts ⇒ faisant autorité) dont `counts.phases` et
|
||||
# `counts.confirmations` sont RECOMPTÉS à chaque build (phases = |graph.phase_order| ·
|
||||
# confirmations = |confirmations_ouvertes|, l'ensemble consolidé des paramètres
|
||||
# réglementaires non confirmés). Deux nombres de cette synthèse sont recopiés À LA
|
||||
# MAIN : le compte de PHASES dans la fiche agent DevOps (03_agents/devops/AGENT.md —
|
||||
# « graphe de **7 phases ordonnées** » ET, plus bas, « exécute les 7 phases sur le
|
||||
# VPS ») et le compte de CONFIRMATIONS dans le README du module
|
||||
# (05_deliverables_mvp/devops/deploy_runbook/README.md — « renseigner les 7
|
||||
# confirmations »). Piège « aveugle » caractéristique (cf. bascule verdict audit_5d,
|
||||
# compensation SEO entre langues) : phases ET confirmations valent 7 AUJOURD'HUI —
|
||||
# un gate qui ne recompterait qu'UN nombre serait AVEUGLE à la dérive de l'autre
|
||||
# (ajouter une phase au graphe → 8 phases pendant que les confirmations restent 7,
|
||||
# ou l'inverse). On RECOMPUTE donc CHAQUE nombre depuis SON PROPRE champ du MANIFEST
|
||||
# (jamais figé ici · zéro duplication du compteur du générateur #5) et on exige
|
||||
# l'égalité avec les docs. Bonus anti-dérive : l'ÉNUMÉRATION des confirmations du
|
||||
# README (les tokens `snake_case` de la phrase « Confirmations préalables VPS »)
|
||||
# doit être l'ensemble EXACT de confirmations_ouvertes — capte l'ÉCHANGE d'un nom de
|
||||
# confirmation (count aveugle à un swap 1↔1). Cohérence croisée : counts.phases ==
|
||||
# |phase_order| · counts.confirmations == |confirmations_ouvertes|. Un claim absent
|
||||
# échoue AUSSI (la disparition d'un chiffre est une régression · traçabilité).
|
||||
# ============================================================================
|
||||
DR_RD = f"{D}/devops/deploy_runbook/README.md"
|
||||
DR_FI = "03_agents/devops/AGENT.md"
|
||||
try:
|
||||
dr_man = load("devops/deploy_runbook/out/MANIFEST.json")
|
||||
dr_ph = str(dr_man["counts"]["phases"])
|
||||
dr_cf = str(dr_man["counts"]["confirmations"])
|
||||
dr_order = dr_man["graph"]["phase_order"]
|
||||
dr_open = dr_man["confirmations_ouvertes"]
|
||||
if dr_ph != str(len(dr_order)):
|
||||
bad(f"DevOps · incohérence artefact : counts.phases={dr_ph} MAIS "
|
||||
f"|graph.phase_order|={len(dr_order)}")
|
||||
if dr_cf != str(len(dr_open)):
|
||||
bad(f"DevOps · incohérence artefact : counts.confirmations={dr_cf} MAIS "
|
||||
f"|confirmations_ouvertes|={len(dr_open)}")
|
||||
except (OSError, KeyError, ValueError) as e:
|
||||
bad(f"DevOps · artefact deploy_runbook illisible/inattendu : {e}"); dr_ph = None
|
||||
if dr_ph is not None:
|
||||
# 1) Fiche agent DevOps — « graphe de N phases ordonnées »
|
||||
dr_fi = read_norm(DR_FI)
|
||||
if dr_fi is not None:
|
||||
m = re.search(r"graphe de \*\*(\d+) phases ordonnées\*\*", dr_fi)
|
||||
if m is None:
|
||||
bad(f"DevOps · {DR_FI} — claim « graphe de N phases ordonnées » INTROUVABLE "
|
||||
f"(attendu phases={dr_ph})")
|
||||
elif m.group(1) == dr_ph:
|
||||
good(f"DevOps · {DR_FI} — {m.group(1)} phases == artefact (phases={dr_ph})")
|
||||
else:
|
||||
bad(f"DevOps · {DR_FI} — dit {m.group(1)} phases MAIS artefact dit {dr_ph} "
|
||||
f"→ régénérer la fiche")
|
||||
# 2) Fiche agent DevOps — 2e transcription « exécute les N phases »
|
||||
m = re.search(r"exécute les (\d+) phases", dr_fi)
|
||||
if m is None:
|
||||
bad(f"DevOps · {DR_FI} — 2e claim « exécute les N phases » INTROUVABLE "
|
||||
f"(attendu phases={dr_ph})")
|
||||
elif m.group(1) == dr_ph:
|
||||
good(f"DevOps · {DR_FI} — « exécute les {m.group(1)} phases » == artefact "
|
||||
f"(phases={dr_ph})")
|
||||
else:
|
||||
bad(f"DevOps · {DR_FI} — 2e claim dit {m.group(1)} phases MAIS artefact dit "
|
||||
f"{dr_ph} → régénérer la fiche")
|
||||
# 3) README module — « renseigner les N confirmations » (compte)
|
||||
dr_rd = read_norm(DR_RD)
|
||||
if dr_rd is not None:
|
||||
m = re.search(r"renseigner les (\d+) confirmations", dr_rd)
|
||||
if m is None:
|
||||
bad(f"DevOps · {DR_RD} — claim « renseigner les N confirmations » INTROUVABLE "
|
||||
f"(attendu confirmations={dr_cf})")
|
||||
elif m.group(1) == dr_cf:
|
||||
good(f"DevOps · {DR_RD} — {m.group(1)} confirmations == artefact "
|
||||
f"(confirmations={dr_cf})")
|
||||
else:
|
||||
bad(f"DevOps · {DR_RD} — dit {m.group(1)} confirmations MAIS artefact dit "
|
||||
f"{dr_cf} → régénérer le README")
|
||||
# 4) README module — l'ÉNUMÉRATION doit être l'ensemble EXACT des confirmations
|
||||
seg = re.search(r"Confirmations préalables VPS\*\* \(consolidées, sourcées\) : "
|
||||
r"(.+?) ## ", dr_rd)
|
||||
if seg is None:
|
||||
bad(f"DevOps · {DR_RD} — énumération « Confirmations préalables VPS » "
|
||||
f"INTROUVABLE (attendu {sorted(dr_open)})")
|
||||
else:
|
||||
listed = sorted(set(re.findall(r"`([a-z_]+)`", seg.group(1))))
|
||||
if listed == sorted(dr_open):
|
||||
good(f"DevOps · {DR_RD} — énumération ({len(listed)}) == confirmations_ouvertes")
|
||||
else:
|
||||
bad(f"DevOps · {DR_RD} — énumération {listed} ≠ confirmations_ouvertes "
|
||||
f"{sorted(dr_open)} → régénérer le README")
|
||||
|
||||
sys.exit(1 if FAIL else 0)
|
||||
PY
|
||||
rc=$?
|
||||
|
||||
Reference in New Issue
Block a user