diff --git a/05_activity_log/2026-08-01.md b/05_activity_log/2026-08-01.md index 8675101..cfd24d3 100644 --- a/05_activity_log/2026-08-01.md +++ b/05_activity_log/2026-08-01.md @@ -2313,3 +2313,69 @@ Working tree byte-restauré via `git checkout -- ` (**JAMAIS** édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact ; l'exécution réelle de l'audit `bench` reste côté VPS). - **Auto-score 4Big** : 96/100. + +## Session `20260801_213444` · Buffer S8 · Fiche ONAPI/Legal : 2ᵉ SURFACE PROSE des ENTITÉS PORTEUSES + +**Tâche** : **Sprint 8 · buffer** (DevOps CI/CD · QA — anti-dérive doc). Roadmap +plancher atteinte ; on continue de fermer les surfaces prose data-derived encore +ungatées. Cible de cette session : `03_agents/onapi_legal/AGENT.md:54`. + +**Surface trouvée (ungatée)** : la fiche ONAPI/Legal décrit le champ Select +`entite_porteuse` du DocType CONFOTUR par « **les 7 entités de CLAUDE.md #Entités +(WAF · WA SRL · AC Arias Cuevas · Consortium ECR DR · Helios RD · Ploutos · 9060 +QC)** ». C'est un **COMPTE littéral** (« 7 ») **+** l'**énumération complète** — une +**SECONDE surface prose** du même menu que le champ `entite_porteuse` offrira. + +Le bloc « Confotur entités » déjà en place ancre le champ à CLAUDE.md §Entités et +gate la ligne du README `legal/confotur` (surface **(e)**), mais ce motif est ancré +sur `05_deliverables_mvp/legal/confotur/README.md` — **il ne vise pas `03_agents/`**. +La fiche restait donc **hors de tout gate** : recompter la §Entités (aujourd'hui 7) +n'atteignait jamais cette phrase. + +**Dérive silencieuse fermée** : AJOUTER / RETIRER / RENOMMER une entité dans +CLAUDE.md §Entités (source ancre) → régénérer propage le menu de l'artefact **et** la +ligne du README confotur, MAIS la fiche onapi_legal se **périme en silence** — elle +annoncerait « 7 entités » quand il y en a 8, ou nommerait `Ploutos` là où le menu ne +l'offre plus. L'agent ERPNext Backend qui seede le DocType croirait le déroulant +couvrir un périmètre d'entités qu'il ne couvre plus (l'**invention #6** interdit). +Aucune suite `tests/` (FONCTIONS de build du DocType, pas la prose d'une fiche) ne +l'attrape. **État courant** : la fiche == §Entités == menu artefact exactement +(rien à réécrire · #6) ; le défaut était la surface **ungatée**. + +**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « Fiche ONAPI/Legal · +2ᵉ SURFACE PROSE des ENTITÉS PORTEUSES », inséré juste après le bloc « Confotur +entités » et avant le bloc Chat OTOIA) : on **re-dérive** la liste depuis les tokens +en gras de la section « ## Entités » de CLAUDE.md (source ancre · zéro duplication) +et on exige de la fiche — **(a)** le COMPTE littéral (« les N entités ») == +|§Entités| ; **(b)** l'énumération == §Entités **ORDRE EXACT** (absent ET fantôme +mordus par set-diff + comparaison ordonnée) ; **(c)** l'ancre « CLAUDE.md #Entités » +citée sur la ligne ; **(d)** cross-check bonus — l'énumération == options +`entite_porteuse` de l'artefact CONFOTUR byte-gaté (la fiche décrit **exactement** le +menu que le DocType offrira). Ligne absente = échec (traçabilité #6). + +**6 morsures vérifiées** (5 côté fiche · 1 côté artefact) : +1. compte `7`→`8` → ✗ (a) « fiche dit 8 entités MAIS §Entités en porte 7 » ; +2. `Ploutos` retiré de l'énumération → ✗ (b) « absent(s)=['Ploutos'] » ; +3. entité FANTÔME `Fantom SA` ajoutée → ✗ (b) « fantôme(s)=['Fantom SA'] » ; +4. réordre `WAF`↔`WA SRL` → ✗ (b) « ordre : fiche […] ≠ §Entités […] » ; +5. ancre `#Entités` retirée de la ligne → ✗ (c) « ne cite pas CLAUDE.md #Entités » ; +6. option `Ploutos`→`Ploutos SRL` dans l'artefact CONFOTUR → ✗ (d) « fiche […] ≠ + menu artefact […] → la fiche décrit un menu que le DocType n'offre pas ». +Restauré = green : (a)(b)(c)(d) tous ✓ · exit 0. Working tree byte-restauré via +`git checkout -- ` (**JAMAIS** `git checkout .` / `git clean`, +interdits absolus). **7 gates re-verts** : `validate_json` / `guard_constraints` / +`check_docs` / `check_artifacts` / `check_regression` / `check_ci_integrity` / +`check_readme_claims` (tous rc=0). + +- `ci/README.md` mis à jour : paragraphe « Entités porteuses du DocType CONFOTUR » + étendu d'une clause « **2ᵉ surface prose (fiche ONAPI/Legal)** » (compte + liste + + cross-check menu artefact). +- **Note anti-stale** : la mémoire `agent-fiche-role-attrs-ungated` est désormais + **périmée** — les attributs des fiches mobile / rendu / ifc_speckle (niveau, scope, + modules, permissions, entité) SONT déjà gatés (blocs « Fiche Mobile » et « Fiches + Faisabilité »). La classe résiduelle réelle est celle des LISTES data-derived + transcrites en prose de fiche, dont cette session ferme l'occurrence entités. +- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ; + édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact ; l'application + réelle du DocType par `bench` reste côté agent ERPNext Backend). +- **Auto-score 4Big** : 96/100. diff --git a/ci/README.md b/ci/README.md index d29812b..6eb8249 100644 --- a/ci/README.md +++ b/ci/README.md @@ -295,7 +295,15 @@ chaque entité) ; **(d)** wiring intact (`options_source == "entites"` · le dé nomme chaque entité (nom complet **ou** 1er token — l'abréviation « AC » de « AC Arias Cuevas » est un choix éditorial toléré · #6). Renommer une entité dans CLAUDE.md seul mord **simultanément** l'artefact, le spec, sa déclaration **et** le -README — l'ancre est vive. +README — l'ancre est vive. **2ᵉ surface prose (fiche ONAPI/Legal)** : la fiche +`03_agents/onapi_legal/AGENT.md:54` ÉNUMÈRE à la main « les **7 entités** de +CLAUDE.md #Entités (…) » — un COMPTE littéral + la liste complète que le motif (e), +ancré sur le README `legal/confotur`, ne balaie pas. On re-dérive la liste de +CLAUDE.md §Entités et on exige de la fiche : **(a)** le compte littéral == |§Entités| +· **(b)** l'énumération == §Entités **ordre exact** (absent ET fantôme mordus par +set-diff) · **(c)** l'ancre « CLAUDE.md #Entités » citée · **(d)** cross-check — +l'énumération == options `entite_porteuse` de l'artefact byte-gaté (la fiche décrit +**exactement** le menu que le DocType offrira). **Champs `dossier_vente`/`estado` du DocType CONFOTUR dérivés du workflow vente.** La §« Cœur du livrable : cross-cohérence » du même README `legal/confotur` affirme diff --git a/ci/check_readme_claims.sh b/ci/check_readme_claims.sh index 12d20ae..f46dc1c 100755 --- a/ci/check_readme_claims.sh +++ b/ci/check_readme_claims.sh @@ -6083,6 +6083,106 @@ if cm_ents is not None: good(f"Confotur entités · (e) README cite CLAUDE.md + nomme les " f"{len(cm_ents)} entités") +# ============================================================================ +# Fiche ONAPI/Legal (03_agents/onapi_legal/AGENT.md:54) · 2e SURFACE PROSE des +# ENTITÉS PORTEUSES du DocType CONFOTUR — la fiche ÉNUMÈRE À LA MAIN « les 7 +# entités de CLAUDE.md #Entités (WAF · WA SRL · AC Arias Cuevas · Consortium ECR +# DR · Helios RD · Ploutos · 9060 QC) ». +# ---------------------------------------------------------------------------- +# Le bloc « Confotur entités » ci-dessus ancre le champ Select `entite_porteuse` +# (options de l'artefact · spec[entites] · entites_source · wiring) et gate la +# ligne du README confotur (surface e, ancrée sur `{D}/legal/confotur/README.md`). +# Mais la FICHE agent onapi_legal:54 est une SECONDE surface prose du MÊME menu — un +# COMPTE littéral « les 7 entités » ET l'énumération complète — qu'AUCUN bloc +# n'atteint (le motif (e) ne vise pas 03_agents/). Piège #6 : AJOUTER / RETIRER / +# RENOMMER une entité dans CLAUDE.md §Entités (source ancre) puis régénérer propage +# le menu de l'artefact ET la ligne du README confotur, MAIS la fiche onapi_legal +# reste PÉRIMÉE en silence : elle annoncerait « 7 entités » quand il y en a 8, ou +# nommerait `Ploutos` là où le menu ne l'offre plus — l'agent ERPNext Backend qui +# seede le DocType croirait le déroulant couvrir un périmètre d'entités qu'il ne +# couvre plus (l'invention #6 interdit). Aucune suite tests/ (FONCTIONS de build du +# DocType, pas la prose d'une fiche) n'attrape ce « vert trompeur » — même patron +# d'IDENTITÉ D'UNE LISTE que la marque SEO §Entités, la §4 du RBAC SPEC ou la ligne +# README (e) ci-dessus. On RE-DÉRIVE la liste depuis CLAUDE.md §Entités (source +# ancre · zéro duplication) et on exige de la fiche : (a) le COMPTE littéral == +# |§Entités| ; (b) l'énumération == §Entités ORDRE EXACT (absent ET fantôme mordus +# par set-diff + comparaison ordonnée) ; (c) l'ancre « CLAUDE.md #Entités » citée ; +# (d) cross-check bonus — la liste de la fiche == options `entite_porteuse` de +# l'artefact byte-gaté (la fiche décrit EXACTEMENT le menu que le DocType offrira). +# Ligne absente = échec (traçabilité #6). +# ============================================================================ +LEGAL_FI = "03_agents/onapi_legal/AGENT.md" +try: + _cm_le = open("CLAUDE.md", encoding="utf-8").read() + _sec_le = re.search(r"^## Entités\s*\n(.*?)(?=^## )", _cm_le, re.S | re.M) + if not _sec_le: + raise ValueError("section « ## Entités » introuvable") + le_ents = [_nfc(x.strip()) for x in re.findall(r"\*\*([^*]+?)\*\*", _sec_le.group(1))] + if not le_ents: + raise ValueError("aucune entité en gras dans §Entités") + if len(le_ents) != len(set(le_ents)): + raise ValueError(f"entités non uniques : {le_ents}") +except (OSError, ValueError) as e: + bad(f"Fiche ONAPI entités · CLAUDE.md §Entités illisible/inattendu : {e}"); le_ents = None +# menu RÉEL de l'artefact byte-gaté (cross-check d) +try: + _cf_dt_le = load("legal/confotur/out/doctype_confotur_application.json") + _ep_le = [f for f in _cf_dt_le.get("fields", []) if f.get("fieldname") == "entite_porteuse"] + le_art_opts = ([_nfc(o.strip()) for o in str(_ep_le[0].get("options", "")).split("\n") if o.strip()] + if len(_ep_le) == 1 else None) +except (OSError, KeyError, ValueError, TypeError) as e: + bad(f"Fiche ONAPI entités · artefact confotur illisible/inattendu : {e}"); le_art_opts = None +le_txt = read_norm(LEGAL_FI) +if le_ents is not None and le_txt is not None: + m_le = re.search(r"\*\*`entite_porteuse`\*\*.{0,60}?les (\d+) entités de (.+?)\((.+?)\)", + le_txt) + if m_le is None: + bad(f"Fiche ONAPI entités · {LEGAL_FI} — ligne `entite_porteuse` " + f"(« les N entités de CLAUDE.md #Entités (…) ») INTROUVABLE/périmée " + f"(claim absent = régression #6)") + else: + # (a) COMPTE littéral == |§Entités| + n_claim = int(m_le.group(1)) + if n_claim == len(le_ents): + good(f"Fiche ONAPI entités · (a) compte « {n_claim} entités » == " + f"CLAUDE.md §Entités") + else: + bad(f"Fiche ONAPI entités · (a) fiche dit « {n_claim} entités » MAIS " + f"CLAUDE.md §Entités en porte {len(le_ents)} → fiche périmée, régénérer") + # (b) énumération == §Entités, ORDRE EXACT (absent ET fantôme mordus) + fi_ents = [_nfc(x.strip()) for x in m_le.group(3).split("·")] + if fi_ents == le_ents: + good(f"Fiche ONAPI entités · (b) énumération == CLAUDE.md §Entités " + f"(ordre exact · {len(fi_ents)})") + else: + _abs = [e for e in le_ents if e not in fi_ents] + _pha = [e for e in fi_ents if e not in le_ents] + _detail = [] + if _abs: + _detail.append(f"absent(s)={_abs}") + if _pha: + _detail.append(f"fantôme(s)={_pha}") + if not _detail: + _detail.append(f"ordre : fiche {fi_ents} ≠ §Entités {le_ents}") + bad(f"Fiche ONAPI entités · (b) énumération ≠ CLAUDE.md §Entités " + f"({' · '.join(_detail)}) → aligner la fiche") + # (c) l'ancre « CLAUDE.md #Entités » est bien citée sur la ligne + if "CLAUDE.md" in m_le.group(2) and "#Entités" in m_le.group(2): + good(f"Fiche ONAPI entités · (c) ancre « CLAUDE.md #Entités » citée") + else: + bad(f"Fiche ONAPI entités · (c) la ligne `entite_porteuse` ne cite pas " + f"l'ancre « CLAUDE.md #Entités » (source={m_le.group(2)!r})") + # (d) cross-check bonus — la liste de la fiche == menu RÉEL de l'artefact + if le_art_opts is None: + bad(f"Fiche ONAPI entités · (d) champ `entite_porteuse` non-singleton " + f"dans l'artefact → menu réel indéterminable") + elif fi_ents == le_art_opts: + good(f"Fiche ONAPI entités · (d) énumération == options `entite_porteuse` " + f"de l'artefact (la fiche décrit le menu réel du DocType)") + else: + bad(f"Fiche ONAPI entités · (d) fiche {fi_ents} ≠ menu artefact " + f"{le_art_opts} → la fiche décrit un menu que le DocType n'offre pas") + # ============================================================================ # Chat OTOIA · l'IDENTITÉ de la persona (`Amélie` / voix `multilingual_v2`) et # des 4 CAPABILITIES OTOIA (`aec.py` + `knowledge.py` + `prompt_engine.py` +