Files
oto-enterprise-os-dtp/05_deliverables_mvp/demo/scenarios/scenlib/builder.py
T
Claude Code DTP Worker cac65386ab [DTP-Worker] Sprint 7 · Générateur Scénarios démo (run-sheet P07 banquier / P05 client) (CRM+Faisabilité · roadmap L68)
Orchestrateur méta-niveau : compose les hand-offs out/ des 10 modules déjà
livrés en un run-sheet de démo jouable, via pointeurs RFC 6901 résolus à la
construction (zéro chiffre en dur · #6). Réutilise le validateur Publiciste et
la preuve de couverture CI de l'auditeur 4Big (#5) — une démo ne s'appuie que
sur des modules gated. Enregistre demo/scenarios dans l'audit 4Big (17→18,
verdict PASS 18/18 à 100). 32 tests (9 injections négatives) · régression 474
tests verts · guards CI verts · builds déterministes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 10:39:24 +00:00

124 lines
4.4 KiB
Python

"""Assemblage déterministe du run-sheet démo + du manifeste (hand-off).
Le builder est PUR : mêmes entrées (spec + artefacts `out/` sur disque) ⇒ même
sortie. Aucun horodatage implicite, aucun ordre non déterministe. L'ordre des
beats est dérivé de la position dans le spec (jamais d'un champ `ordre` en dur,
pour éviter toute dérive). Les modules cités sont recoupés avec l'ensemble des
modules gated par le CI (via `deps.ci_module_paths`) — une démo ne s'appuie que
sur du validé.
"""
from __future__ import annotations
from . import deps, evidence
SELF_MODULE = "demo/scenarios"
def _build_beat(loader: evidence.Loader, beat: dict, ordre: int) -> dict:
cites = []
for ev in beat["evidence"]:
module, file, pointer = ev["module"], ev["file"], ev["pointer"]
valeur, resolu = evidence.resolve(loader, module, file, pointer)
cites.append({
"module": module,
"file": file,
"pointer": pointer,
"label": ev["label"],
"valeur": valeur,
"resolu": resolu,
})
return {
"ordre": ordre,
"id": beat["id"],
"titre": beat["titre"],
"role_narratif": beat["role_narratif"],
"duree_min": beat["duree_min"],
"evidence": cites,
}
def _build_scenario(loader: evidence.Loader, spec: dict, sc: dict) -> dict:
beats = [_build_beat(loader, b, i + 1) for i, b in enumerate(sc["beats"])]
modules = sorted({c["module"] for b in beats for c in b["evidence"]})
duree = sum(b["duree_min"] for b in beats)
return {
"id": sc["id"],
"projet": sc["projet"],
"projet_libelle": spec["projets"][sc["projet"]],
"audience": sc["audience"],
"titre": sc["titre"],
"objectif": sc["objectif"],
"duree_min": duree,
"modules_cites": modules,
"beats": beats,
}
def build_run_sheet(spec: dict, generated_at: str | None = None) -> dict:
"""Construit le run-sheet complet (le hand-off principal)."""
loader = evidence.Loader()
scenarios = [_build_scenario(loader, spec, sc) for sc in spec["scenarios"]]
return {
"doc": "OTO · Run-sheet démo (méta-livrable)",
"spec_version": spec["spec_version"],
"roadmap_ref": spec["roadmap_ref"],
"generated_at": generated_at,
"scenarios": scenarios,
}
def _all_citations(run_sheet: dict) -> list[dict]:
return [c for sc in run_sheet["scenarios"] for b in sc["beats"] for c in b["evidence"]]
def build_manifest(spec: dict, run_sheet: dict) -> dict:
"""Manifeste diagnostic + preuve de couverture CI + drapeau `pret`."""
cites = _all_citations(run_sheet)
non_resolues = [c for c in cites if not c["resolu"]]
modules_cites = sorted({c["module"] for c in cites})
gated = deps.ci_module_paths() # modules réellement gated par le CI
non_gated = sorted(m for m in modules_cites if m not in gated)
scen_diag = []
for sc in run_sheet["scenarios"]:
sc_cites = [c for b in sc["beats"] for c in b["evidence"]]
scen_diag.append({
"id": sc["id"],
"projet": sc["projet"],
"audience": sc["audience"],
"beats": len(sc["beats"]),
"citations": len(sc_cites),
"citations_resolues": sum(1 for c in sc_cites if c["resolu"]),
"duree_min": sc["duree_min"],
"modules_cites": sc["modules_cites"],
})
couverture_ok = not non_gated
pret = (not non_resolues) and couverture_ok
return {
"deliverable": "Scénarios démo · run-sheet de pitch",
"sprint": "Sprint 7 · CRM + Faisabilité",
"roadmap_ref": spec["roadmap_ref"],
"generated_from": "scenario_spec.json",
"deliverables_root": "05_deliverables_mvp",
"self_module": SELF_MODULE,
"generated_at": run_sheet["generated_at"],
"scenarios": scen_diag,
"counts": {
"scenarios": len(run_sheet["scenarios"]),
"beats_total": sum(len(sc["beats"]) for sc in run_sheet["scenarios"]),
"citations_total": len(cites),
"citations_non_resolues": len(non_resolues),
"modules_cites_uniques": len(modules_cites),
},
"modules_cites": modules_cites,
"couverture_ci": {
"ok": couverture_ok,
"modules_cites_non_gated": non_gated,
},
"pret": pret,
}