"""Ancrages hors-module du PIE manifest (anti-dérive · anti-invention #6). Le manifest PIE ne se valide pas seulement contre lui-même : trois faits sont RE-DÉRIVÉS de sources de vérité EXTERNES au module, jamais recopiés à la main — 1. Les codes projets (`brands[].code`) sont confrontés à `CLAUDE.md §Projets` (la constitution · [[claude-md-constant-anchor-gate]]). Une marque PIE dont le code ne figure pas dans la constitution est une invention refusée. 2. `annexe` (12) et `version_workflow` (V10.1) doivent apparaître VERBATIM dans la directive PIE. Renuméroter l'annexe dans la directive sans régénérer ici casse la validation ([[roadmap-anchor-gate]] appliqué à la directive). 3. Chaque `downstream_registry[].module` non nul doit être un répertoire de livrable RÉEL sous `05_deliverables_mvp/` (comme fiscal/ecf vérifie ses contrats voisins). Un module fantôme (renommé/supprimé) casse la validation. Zéro dépendance pip · zéro réseau · aucune écriture VPS (#8) : lecture seule de fichiers déjà versionnés dans le dépôt. """ from __future__ import annotations import os import re _HERE = os.path.dirname(os.path.abspath(__file__)) _MODULE_DIR = os.path.normpath(os.path.join(_HERE, "..")) # pie/manifest/ DELIVERABLES_ROOT = os.path.normpath(os.path.join(_HERE, "..", "..", "..")) # 05_deliverables_mvp/ _REPO_ROOT = os.path.normpath(os.path.join(DELIVERABLES_ROOT, "..")) _CLAUDE_MD = os.path.join(_REPO_ROOT, "CLAUDE.md") def claude_md_project_codes() -> set[str]: """Codes Pxx de la ligne `## Projets` de CLAUDE.md (source de vérité #Projets).""" with open(_CLAUDE_MD, encoding="utf-8") as fh: text = fh.read() # Section entre « ## Projets » et le prochain titre « ## ». m = re.search(r"^##\s+Projets\s*\n(.*?)(?:\n##\s|\Z)", text, re.S | re.M) section = m.group(1) if m else "" return set(re.findall(r"\bP0[1-9]\b", section)) def directive_declares(directive_relpath: str, *needles: str) -> dict[str, bool]: """Vrai/faux, pour chaque `needle`, s'il apparaît verbatim dans la directive PIE.""" path = os.path.join(_REPO_ROOT, directive_relpath) try: with open(path, encoding="utf-8") as fh: text = fh.read() except OSError: return {n: False for n in needles} return {n: (n in text) for n in needles} def module_exists(rel_module: str) -> bool: """Le module de livrable `rel_module` (ex. 'faisabilite/bancable') existe-t-il ?""" return os.path.isdir(os.path.join(DELIVERABLES_ROOT, rel_module)) def module_out_dir() -> str: """Répertoire de sortie du module (le SEUL endroit où ce worker écrit · #8).""" return os.path.join(_MODULE_DIR, "out")