[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:
@@ -2606,3 +2606,61 @@ verrouillés) · `guard_constraints` · `validate_json`.
|
|||||||
`validate_json`).
|
`validate_json`).
|
||||||
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo).
|
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo).
|
||||||
- **Auto-score 4Big** : 96/100.
|
- **Auto-score 4Big** : 96/100.
|
||||||
|
|
||||||
|
## 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)
|
||||||
|
- **Constat + DÉFAUT RÉEL capté** : le README `devops/deploy_runbook` porte la table
|
||||||
|
« ## Plan de phases généré » (README:45-53) — les **7 phases ordonnées** que
|
||||||
|
l'agent DevOps suit phase par phase au portage VPS. Ses colonnes d'identité sont
|
||||||
|
data-derived de `out/deploy_runbook.json` : `#`=`order`, `Responsable`=`responsable`
|
||||||
|
(`worker`/`vps`/`worker+vps`), **Modules**=l'ensemble des modules gated déployés,
|
||||||
|
`Dépend de`=les n°s d'ordre des `depends_on`. Le bloc DevOps existant de
|
||||||
|
`check_readme_claims.sh` ne gate QUE **deux comptes agrégés** (phases ×2 dans la
|
||||||
|
fiche `devops` · confirmations dans le README) — ces quatre colonnes étaient un
|
||||||
|
**WILDCARD**. Contrairement aux surfaces buffer précédentes (« aucune valeur
|
||||||
|
périmée »), ici la table AVAIT **DÉRIVÉ** : elle **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** (`counts.modules
|
||||||
|
== modules_mapped == 22`). Vert trompeur caractéristique : l'agent DevOps suivant la
|
||||||
|
table aurait **sauté 2 modules** au déploiement.
|
||||||
|
- **Data-derived** : `out/deploy_runbook.json` (7 phases · `order`/`id`/`responsable`/
|
||||||
|
`depends_on`/`modules[]`) et sa source `deploy_spec.json.module_phase` (22 modules,
|
||||||
|
autorité byte-gatée par `check_artifacts` · couverture bijective prouvée vs jobs CI).
|
||||||
|
`check_artifacts` ne prouve QUE `deploy_runbook==build` — l'identité des LIGNES de la
|
||||||
|
table (qui déploie quoi · dans quel ordre · après quoi) restait non gatée.
|
||||||
|
- **Preuve reproduite** : retirer `mobile/app_config` de la phase 5 du README →
|
||||||
|
`check_readme_claims` **exit 0** avant le gate (le bloc ne voyait que phases=7 &
|
||||||
|
confirmations=7). Un responsable périmé (phase VPS attribuée au worker) ou une
|
||||||
|
dépendance périmée (Frontend avant RBAC) est un hazard réel qu'aucune suite `tests/`
|
||||||
|
(qui teste des fonctions, pas la table commitée) n'attrape.
|
||||||
|
- **Correction d'abord (artefact = autorité · #6)** : la table étant STALE, README
|
||||||
|
corrigé pour matcher l'artefact byte-gaté — phase 5 `+mobile/app_config` (& libellé
|
||||||
|
« + config mobile »), phase 7 `+qa/acceptance`. Consommateur régénéré : le
|
||||||
|
`qa/audit_4big/out/quality_report.json` enregistre la taille en octets de chaque
|
||||||
|
README en `evidence` (5414→5468 pour deploy_runbook) — rebuild `audit_4big` (score/
|
||||||
|
verdict/totaux INCHANGÉS, seule l'évidence-taille bouge · reproducibility gate).
|
||||||
|
- **Gate ajouté** (`ci/check_readme_claims.sh`, sous-bloc « 2bis) deploy_runbook —
|
||||||
|
TABLE Plan de phases ») : (1) **IDENTITÉ par phase** — chaque ligne porte EXACTEMENT
|
||||||
|
`#`=`order` + `Responsable`=`responsable` + **Modules** (ensemble de code-spans) ==
|
||||||
|
modules de la phase + `Dépend de`=n°s d'ordre des `depends_on` (deps extraits par
|
||||||
|
digits ⇒ robuste au séparateur) ; colonne « Phase » libre (paraphrase). (2)
|
||||||
|
**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 — le défaut même capté). Cohérences croisées
|
||||||
|
(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). Un claim absent échoue AUSSI.
|
||||||
|
- **8 morsures vérifiées** : README phase5 omet `mobile/app_config` (défaut original,
|
||||||
|
`manquants=[mobile/app_config]`) · README phase7 omet `qa/acceptance` (défaut
|
||||||
|
original) · README phase5 responsable `worker+vps`→`vps` (câblage) · README phase6
|
||||||
|
`Dépend de 4,5`→`4` (dépendance manquante) · module FANTÔME `ghost/mod` en phase5
|
||||||
|
(`en_trop`) · ligne FANTÔME `#8` (8≠7) · phase 6 SUPPRIMÉE (INTROUVABLE + 6≠7) ·
|
||||||
|
artefact phase2 responsable `vps`→`worker` (README stale). Restauré = green :
|
||||||
|
7 lignes == 7 phases · union Modules 22==22 · responsables/deps/modules == artefact ·
|
||||||
|
ordres 1..7 contigus · exit 0.
|
||||||
|
- **État courant** : le défaut STALE est **corrigé** (README byte-aligné sur
|
||||||
|
l'artefact) ET la surface est désormais **gatée**. `ci/README.md` (ligne synthèse +
|
||||||
|
détail « 2ᵉ surface `devops/deploy_runbook` ») mis à jour · **7 gates verts**
|
||||||
|
(`check_readme_claims` · `check_artifacts` · `check_docs` · `check_ci_integrity` ·
|
||||||
|
`check_regression` · `guard_constraints` · `validate_json`).
|
||||||
|
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo).
|
||||||
|
- **Auto-score 4Big** : 96/100.
|
||||||
|
|||||||
@@ -48,9 +48,9 @@ chiffrée dans le catalogue de confirmations.
|
|||||||
| 2 | vps | DocTypes custom | `crm/dossier_vente`, `legal/confotur` | 1 |
|
| 2 | vps | DocTypes custom | `crm/dossier_vente`, `legal/confotur` | 1 |
|
||||||
| 3 | worker+vps | RBAC (Role + DocPerm + UP + Role Profile) | `rbac/fixtures_gen`, `rbac/userperm_gen`, `rbac/roleprofile_gen`, `rbac/apply_plan` | 2 |
|
| 3 | worker+vps | RBAC (Role + DocPerm + UP + Role Profile) | `rbac/fixtures_gen`, `rbac/userperm_gen`, `rbac/roleprofile_gen`, `rbac/apply_plan` | 2 |
|
||||||
| 4 | worker+vps | Workflow vente + règles métier | `crm/workflow_vente`, `crm/commissions`, `fiscal/ecf_dgii` | 2, 3 |
|
| 4 | worker+vps | Workflow vente + règles métier | `crm/workflow_vente`, `crm/commissions`, `fiscal/ecf_dgii` | 2, 3 |
|
||||||
| 5 | worker+vps | Frontend desk (Workspaces + chat OTOIA) | `frontend/portails`, `frontend/chat_otoia` | 3 |
|
| 5 | worker+vps | Frontend desk (Workspaces + chat OTOIA) + config mobile | `frontend/portails`, `frontend/chat_otoia`, `mobile/app_config` | 3 |
|
||||||
| 6 | worker+vps | Contenu & publication | `publiciste`, `faisabilite/generator`, `faisabilite/bancable`, `seo`, `demo/scenarios` | 4, 5 |
|
| 6 | worker+vps | Contenu & publication | `publiciste`, `faisabilite/generator`, `faisabilite/bancable`, `seo`, `demo/scenarios` | 4, 5 |
|
||||||
| 7 | vps | Vérification QA post-déploiement | `qa/audit_5d`, `qa/audit_4big`, `qa/regression` | 4, 5, 6 |
|
| 7 | vps | Vérification QA post-déploiement | `qa/acceptance`, `qa/audit_5d`, `qa/audit_4big`, `qa/regression` | 4, 5, 6 |
|
||||||
|
|
||||||
**Confirmations préalables VPS** (consolidées, sourcées) : `custom_modules`,
|
**Confirmations préalables VPS** (consolidées, sourcées) : `custom_modules`,
|
||||||
`custom_doctypes`, `taux_commission` (Direction · audit_5d D1.1), `rnc_emisor`
|
`custom_doctypes`, `taux_commission` (Direction · audit_5d D1.1), `rnc_emisor`
|
||||||
|
|||||||
@@ -816,7 +816,7 @@
|
|||||||
"criterion": "DOC",
|
"criterion": "DOC",
|
||||||
"weight": 20,
|
"weight": 20,
|
||||||
"passed": true,
|
"passed": true,
|
||||||
"evidence": "README.md (5414 octets)"
|
"evidence": "README.md (5468 octets)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"criterion": "CONTRAT",
|
"criterion": "CONTRAT",
|
||||||
|
|||||||
+19
-1
File diff suppressed because one or more lines are too long
@@ -1560,6 +1560,102 @@ if dr_ph is not None:
|
|||||||
bad(f"DevOps · {DR_RD} — énumération {listed} ≠ confirmations_ouvertes "
|
bad(f"DevOps · {DR_RD} — énumération {listed} ≠ confirmations_ouvertes "
|
||||||
f"{sorted(dr_open)} → régénérer le README")
|
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
|
# 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
|
# trompeur », transcrit À LA MAIN dans le diagramme d'architecture du README du
|
||||||
|
|||||||
Reference in New Issue
Block a user