"""Assemblage déterministe du manifest PIE depuis `pie_spec.json`. Entrée : le contrat PIE (taxonomie Project Master Data, registre downstream, règles de synchronisation, workflow 10 étapes, marques, storage layout) — transcrit VERBATIM de la directive PIE (SSOT · #6, aucune valeur inventée). Sortie : un bundle déterministe (tri stable, aucun horodatage) → reproductible bit-à-bit à contrat constant, donc diffable et re-générable en CI ([[artifact-reproducibility-gate]]). Le builder n'ajoute AUCUN chiffre propre à OTO : il résout le `statut` de chaque livrable downstream (« gated » si un module de livrado réel l'implémente déjà, sinon « a_construire ») et un manifeste de traçabilité (comptes dérivés). """ from __future__ import annotations from typing import Any from . import deps def _statut(module: str | None) -> str: """« gated » si un module réel implémente déjà le downstream, sinon « a_construire ».""" return "gated" if module else "a_construire" def build_bundle(spec: dict) -> dict[str, Any]: """Transforme le contrat PIE en manifest de dépendances + manifeste de traçabilité.""" groups = sorted(spec["master_data_groups"], key=lambda g: g["cle"]) registry = [ { "cle": d["cle"], "libelle": d["libelle"], "module": d["module"], "statut": _statut(d["module"]), } for d in sorted(spec["downstream_registry"], key=lambda d: d["cle"]) ] sync_rules = [ { "trigger": r["trigger"], "trigger_group": r["trigger_group"], "downstream": list(r["downstream"]), # ordre directive préservé (§82-86) } for r in sorted(spec["sync_rules"], key=lambda r: r["trigger"]) ] workflow = sorted(spec["workflow"], key=lambda s: s["etape"]) brands = sorted(spec["brands"], key=lambda b: b["code"]) manifest_out = { "engine": spec["engine"], "annexe": spec["annexe"], "version_workflow": spec["version_workflow"], "ssot": spec["ssot"], "source_spec": "pie_spec.json", "master_data_groups": groups, "downstream_registry": registry, "sync_rules": sync_rules, "workflow": workflow, "brands": brands, "storage_layout": spec["storage_layout"], } gated = [d for d in registry if d["statut"] == "gated"] a_construire = [d for d in registry if d["statut"] == "a_construire"] traceability = { "generated_from": "pie_spec.json", "claude_md_source": "CLAUDE.md", "directive_source": spec["directive"], "engine": spec["engine"], "annexe": spec["annexe"], "version_workflow": spec["version_workflow"], "ssot": spec["ssot"], "counts": { "master_data_groups": len(groups), "downstream_total": len(registry), "downstream_gated": len(gated), "downstream_a_construire": len(a_construire), "sync_rules": len(sync_rules), "workflow_steps": len(workflow), "brands": len(brands), }, "modules_gated_references": [ {"cle": d["cle"], "module": d["module"]} for d in gated ], "note_anti_invention": ( "Toute donnée est transcrite de la directive PIE (SSOT · #6) : aucune " "valeur projet (prix, surfaces, marges) n'est fabriquée ici — le PIE " "les EXTRAIT de la faisabilité validée côté VPS (/opt/oto/data/pie/{code}/, " "jamais écrit par ce worker · #8). Les codes projets sont ancrés à " "CLAUDE.md §Projets ; annexe/version à la directive ; les modules " "downstream « gated » à des répertoires de livrables réels." ), } return {"manifest": manifest_out, "traceability": traceability}