[DTP-Worker] Sprint 8 · buffer · DevOps/deploy_runbook (2e surface) : la TABLE « Plan de phases généré » — le GRAPHE de portage VPS ordonné (# · Responsable · Modules · Dépend de) — était un WILDCARD et avait DÉRIVÉ : elle omettait mobile/app_config (phase 5) ET qa/acceptance (phase 7), 20/22 modules listés pendant que la couverture prouvée était bijective 22/22. README corrigé sur l'artefact byte-gaté + gate d'identité par phase (8 morsures).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1560,6 +1560,102 @@ if dr_ph is not None:
|
||||
bad(f"DevOps · {DR_RD} — énumération {listed} ≠ confirmations_ouvertes "
|
||||
f"{sorted(dr_open)} → régénérer le README")
|
||||
|
||||
# 2bis) deploy_runbook — IDENTITÉ de la TABLE « Plan de phases généré » -----------
|
||||
# Le bloc ci-dessus ne gate que DEUX comptes agrégés (phases · confirmations). La
|
||||
# table « ## Plan de phases généré » énumère, PAR phase, QUATRE données data-derived
|
||||
# de out/deploy_runbook.json (byte-gaté par check_artifacts) : le n° d'ordre (#), le
|
||||
# RESPONSABLE (worker/vps/worker+vps), l'ensemble des MODULES gated déployés dans la
|
||||
# phase, et « Dépend de » (les n°s d'ordre des `depends_on`). C'est le GRAPHE DE
|
||||
# PORTAGE VPS ORDONNÉ que l'agent DevOps suit phase par phase — un module OUBLIÉ de
|
||||
# la colonne Modules (donc jamais déployé), un responsable ou une dépendance périmés
|
||||
# (ex. Frontend avant RBAC) sont un hazard réel, invisible à check_artifacts (qui ne
|
||||
# prouve QUE deploy_runbook==build) ET au bloc de comptes (aveugle à l'identité des
|
||||
# lignes ; ces colonnes étaient un WILDCARD). Même classe que la table run-book de
|
||||
# rbac/apply_plan et la table demo/scenarios. Preuve reproduite : la table omettait
|
||||
# `mobile/app_config` (phase 5) ET `qa/acceptance` (phase 7) — 20 modules listés sur
|
||||
# 22 — pendant que le MANIFEST prouvait la couverture BIJECTIVE 22/22 vs CI : vert
|
||||
# trompeur, l'agent DevOps aurait sauté 2 modules. On RECOMPUTE #/responsable/
|
||||
# modules/deps depuis deploy_runbook.json (zéro duplication) et on exige que la table
|
||||
# les énonce EXACTEMENT. Identité d'ensemble : {ordres des lignes} == {ordres de
|
||||
# l'artefact} (aucune phase FANTÔME/MANQUANTE) ET l'UNION des cellules Modules == les
|
||||
# 22 modules de l'artefact (aucun module OUBLIÉ/en trop). La colonne « Phase » reste
|
||||
# libre (paraphrase). Cohérences croisées en bonus (mordent un plan INTERNEMENT
|
||||
# incohérent) : ordres contigus 1..N sans doublon · responsable ∈ {worker, vps,
|
||||
# worker+vps} · toute dépendance pointe en ARRIÈRE (n° < n° de la phase ⇒ pas de
|
||||
# cycle/renvoi avant).
|
||||
try:
|
||||
dr_plan = load("devops/deploy_runbook/out/deploy_runbook.json")
|
||||
except (OSError, KeyError, ValueError) as e:
|
||||
bad(f"DevOps · run-book illisible : {e}"); dr_plan = None
|
||||
if dr_plan is not None:
|
||||
id2order = {p["id"]: p["order"] for p in dr_plan}
|
||||
ALLOWED_RESP = {"worker", "vps", "worker+vps"}
|
||||
orders = sorted(p["order"] for p in dr_plan)
|
||||
all_modules = sorted(x["module"] for p in dr_plan for x in p["modules"])
|
||||
if orders == list(range(1, len(dr_plan) + 1)):
|
||||
good(f"DevOps · run-book — ordres contigus 1..{len(dr_plan)} (aucun trou/doublon)")
|
||||
else:
|
||||
bad(f"DevOps · run-book — ordres NON contigus {orders} "
|
||||
f"(attendu 1..{len(dr_plan)}) → plan incohérent")
|
||||
# Lignes lues en RAW : la table « Plan de phases généré » est la SEULE à 5
|
||||
# colonnes dont la 1ʳᵉ est numérique (l'autre table est `Fichier | Rôle`).
|
||||
try:
|
||||
dr_tbl = open(DR_RD, encoding="utf-8").read().splitlines()
|
||||
except OSError as e:
|
||||
bad(f"DevOps · {DR_RD} illisible : {e}"); dr_tbl = []
|
||||
table_orders, rows_by_order = [], {}
|
||||
for ln in dr_tbl:
|
||||
cells = [c.strip() for c in ln.strip().strip("|").split("|")]
|
||||
if len(cells) == 5 and re.fullmatch(r"\d+", cells[0]):
|
||||
o = int(cells[0]); table_orders.append(o); rows_by_order[o] = cells
|
||||
if sorted(table_orders) == orders and len(table_orders) == len(set(table_orders)):
|
||||
good(f"DevOps · run-book — {len(table_orders)} lignes == {len(dr_plan)} phases "
|
||||
f"(aucune FANTÔME/MANQUANTE)")
|
||||
else:
|
||||
bad(f"DevOps · run-book — lignes table {sorted(table_orders)} ≠ phases artefact "
|
||||
f"{orders} (ligne en trop/manquante ou doublon)")
|
||||
table_modules = sorted(m for r in rows_by_order.values()
|
||||
for m in re.findall(r"`([^`]+)`", r[3]))
|
||||
if table_modules == all_modules:
|
||||
good(f"DevOps · run-book — union des Modules ({len(table_modules)}) == "
|
||||
f"{len(all_modules)} modules de l'artefact (aucun OUBLIÉ/en trop)")
|
||||
else:
|
||||
miss = sorted(set(all_modules) - set(table_modules))
|
||||
extra = sorted(set(table_modules) - set(all_modules))
|
||||
bad(f"DevOps · run-book — colonne Modules ≠ artefact : manquants={miss or '—'} "
|
||||
f"en_trop={extra or '—'} → régénérer le README")
|
||||
for p in sorted(dr_plan, key=lambda x: x["order"]):
|
||||
o, resp = p["order"], p["responsable"]
|
||||
deps = sorted(id2order[x] for x in p["depends_on"])
|
||||
mods = sorted(x["module"] for x in p["modules"])
|
||||
if resp not in ALLOWED_RESP:
|
||||
bad(f"DevOps · phase {o} — responsable artefact « {resp} » "
|
||||
f"hors {sorted(ALLOWED_RESP)} (plan incohérent)")
|
||||
if any(d >= o for d in deps):
|
||||
bad(f"DevOps · phase {o} — dépendance en AVANT {deps} (≥ {o}) : graphe incohérent")
|
||||
row = rows_by_order.get(o)
|
||||
if row is None:
|
||||
bad(f"DevOps · phase {o} — ligne INTROUVABLE dans {DR_RD} "
|
||||
f"(attendu responsable={resp} modules={mods} deps={deps or '—'})"); continue
|
||||
got_resp = row[1]
|
||||
got_mods = sorted(re.findall(r"`([^`]+)`", row[3]))
|
||||
got_deps = sorted(int(x) for x in re.findall(r"\d+", row[4]))
|
||||
if got_resp == resp:
|
||||
good(f"DevOps · phase {o} — responsable « {got_resp} » == artefact")
|
||||
else:
|
||||
bad(f"DevOps · phase {o} — {DR_RD} dit responsable « {got_resp} » MAIS "
|
||||
f"artefact dit « {resp} » → régénérer le README")
|
||||
if got_mods == mods:
|
||||
good(f"DevOps · phase {o} — Modules ({len(got_mods)}) == artefact")
|
||||
else:
|
||||
bad(f"DevOps · phase {o} — {DR_RD} Modules {got_mods} MAIS artefact dit "
|
||||
f"{mods} → régénérer le README")
|
||||
if got_deps == deps:
|
||||
good(f"DevOps · phase {o} — Dépend de {got_deps or '—'} == artefact")
|
||||
else:
|
||||
bad(f"DevOps · phase {o} — {DR_RD} « Dépend de » = {got_deps or '—'} MAIS "
|
||||
f"artefact dit {deps or '—'} → régénérer le README")
|
||||
|
||||
# ============================================================================
|
||||
# Démo · run-sheet de pitch (nombre de modules cités) — même classe de « vert
|
||||
# trompeur », transcrit À LA MAIN dans le diagramme d'architecture du README du
|
||||
|
||||
Reference in New Issue
Block a user