[DTP-Worker] Sprint 7/8 · Livrable démo : prompteur Markdown (out/run_sheet.md)
Le seul livrable démo (demo/scenarios) ne produisait qu'un run-sheet JSON machine — aucun support lisible par un présentateur, alors que le Sprint 7 vise un scénario « prêt à jouer ». Rendu Markdown in-repo depuis le JSON = même pattern que faisabilite (rend des .md), zéro écriture VPS. - scenlib/render.py : render_markdown() PUR/déterministe, ne lit que le run-sheet (déjà anti-inventé), aucun chiffre nouveau (#6). - build émet out/run_sheet.md (prompteur : par beat, table « À dire | Chiffre | Source (preuve) » traçant le pointeur RFC 6901 amont). - Compat vérifiée : HANDOFF 4Big ne json.load que les .json (ignore .md) ; check_artifacts diffe tout fichier build → md commité + reproductible. - Consommateurs régénérés : régression 551→558 (22 suites PASS) ; quality_report.json (README 5011→5575 o · 32→39 test_*) PASS 22/22 ; 03_agents/qa/AGENT.md 551→558 ; README démo auto-score 96→97. - 7 gates verts · 39 tests démo · 34 tests audit · arbre propre. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
"""Rendu Markdown du run-sheet démo — le **prompteur** lisible par un présentateur.
|
||||
|
||||
Transforme PUREMENT le `run_sheet.json` (déjà résolu, déjà anti-inventé) en un
|
||||
script de pitch lisible à voix haute. Contraintes :
|
||||
|
||||
- **Zéro donnée nouvelle (CLAUDE.md #6)** : ce module ne lit QUE le run-sheet ;
|
||||
chaque chiffre du Markdown provient d'une citation `evidence.valeur` déjà
|
||||
résolue depuis un artefact amont. Aucune valeur n'est saisie, calculée ni
|
||||
reformatée en un autre nombre.
|
||||
- **Déterministe** : mêmes entrées ⇒ mêmes octets (aucun horodatage implicite ;
|
||||
`generated_at` est celui, explicite, du run-sheet). C'est ce qui permet au
|
||||
gate `ci/check_artifacts.sh` de prouver la reproductibilité de `out/run_sheet.md`.
|
||||
|
||||
Le rendu final riche (deck, page démo `otov7.com`) reste côté VPS (#8) ; ce
|
||||
prompteur Markdown est le support **texte** in-repo que ce rendu habillera.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from . import evidence
|
||||
|
||||
|
||||
def _fmt(valeur) -> str:
|
||||
"""Formate une valeur de citation pour l'oral, sans jamais inventer.
|
||||
|
||||
Un placeholder non résolu (ne devrait pas survivre aux invariants, mais on
|
||||
reste défensif) est rendu verbatim entre back-ticks — jamais promu en chiffre.
|
||||
"""
|
||||
if isinstance(valeur, bool):
|
||||
return "oui" if valeur else "non"
|
||||
if isinstance(valeur, list):
|
||||
return ", ".join(_fmt(v) for v in valeur)
|
||||
if valeur is None:
|
||||
return "`—`"
|
||||
if evidence.is_placeholder(valeur):
|
||||
return f"`{valeur}`"
|
||||
return str(valeur)
|
||||
|
||||
|
||||
def _scenario_md(sc: dict) -> list[str]:
|
||||
lignes: list[str] = []
|
||||
lignes.append(f"## {sc['titre']}")
|
||||
lignes.append("")
|
||||
lignes.append(
|
||||
f"- **Scénario** : `{sc['id']}` · **audience** : {sc['audience']} · "
|
||||
f"**projet** : {sc['projet']} — {sc['projet_libelle']}"
|
||||
)
|
||||
lignes.append(f"- **Durée** : {sc['duree_min']} min · {len(sc['beats'])} temps forts")
|
||||
lignes.append(f"- **Objectif** : {sc['objectif']}")
|
||||
lignes.append("")
|
||||
for b in sc["beats"]:
|
||||
lignes.append(f"### {b['ordre']}. {b['titre']} · {b['duree_min']} min")
|
||||
lignes.append("")
|
||||
lignes.append(f"> {b['role_narratif']}")
|
||||
lignes.append("")
|
||||
lignes.append("| À dire | Chiffre | Source (preuve) |")
|
||||
lignes.append("|---|---|---|")
|
||||
for c in b["evidence"]:
|
||||
src = f"`{c['module']}` · `{c['file']}#{c['pointer']}`"
|
||||
lignes.append(f"| {c['label']} | {_fmt(c['valeur'])} | {src} |")
|
||||
lignes.append("")
|
||||
return lignes
|
||||
|
||||
|
||||
def render_markdown(run_sheet: dict) -> str:
|
||||
"""Run-sheet dict → prompteur Markdown (chaîne terminée par un saut de ligne)."""
|
||||
lignes: list[str] = []
|
||||
lignes.append(f"# {run_sheet['doc']}")
|
||||
lignes.append("")
|
||||
lignes.append(
|
||||
"> Prompteur généré **automatiquement** depuis `out/run_sheet.json` "
|
||||
"(`demo_scenario_gen build`). Chaque chiffre est une **preuve re-résolue** "
|
||||
"depuis un hand-off `out/` d'un module gaté — jamais saisi à la main "
|
||||
"(anti-invention · CLAUDE.md #6). **Ne pas éditer à la main** : régénérer."
|
||||
)
|
||||
lignes.append("")
|
||||
lignes.append(f"- **Roadmap** : {run_sheet['roadmap_ref']}")
|
||||
lignes.append(f"- **Version du spec** : {run_sheet['spec_version']}")
|
||||
total = sum(sc["duree_min"] for sc in run_sheet["scenarios"])
|
||||
lignes.append(
|
||||
f"- **{len(run_sheet['scenarios'])} scénario(s)** · durée cumulée {total} min"
|
||||
)
|
||||
if run_sheet.get("generated_at"):
|
||||
lignes.append(f"- **Généré le** : {run_sheet['generated_at']}")
|
||||
lignes.append("")
|
||||
lignes.append("---")
|
||||
lignes.append("")
|
||||
for i, sc in enumerate(run_sheet["scenarios"]):
|
||||
lignes += _scenario_md(sc)
|
||||
if i < len(run_sheet["scenarios"]) - 1:
|
||||
lignes.append("---")
|
||||
lignes.append("")
|
||||
return "\n".join(lignes).rstrip("\n") + "\n"
|
||||
Reference in New Issue
Block a user