[DTP-Worker] Sprint 8 · buffer · Faisabilité/generator : ANCRAGE des 3 copies aval des 6 marqueurs canoniques (#9/#10) — l'oracle d'enforcement renderer._CANONICAL_MARKERS (que check_canonical parcourt) ⨯ les 2 docstrings scorer.py ⨯ l'oracle du test test_generator.py, recomputés de CLAUDE.md #9/#10 (réutilise exp)
Le bloc « paramètres canoniques » n'ancrait que model.py::CANONICAL + le README ; son propre commentaire nommait pourtant le défaut (« marqueurs hardcodés dans le test, une copie de plus jamais comparée à CLAUDE.md ») sans le fermer. Nouveau bloc : helper _list_vs_want6 (set-diff EXACT absent/en-trop) pour les copies-LISTES renderer._CANONICAL_MARKERS + oracle du test ; substring normalisé (USD+DOP==USD + DOP) pour les 2 docstrings scorer. 4 morsures vérifiées dont CLAUDE.md #10 Cardnet→Azul mordant SIMULTANÉMENT model+renderer+test+2 docstrings (valeur de l'ancrage) ; restauré vert · 7 gates re-verts. ci/README.md + activity log mis à jour. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1571,3 +1571,66 @@ rôle == RBAC · FormaPago == artefact · exit 0. Working tree byte-restauré
|
|||||||
édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact ; la connexion
|
édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact ; la connexion
|
||||||
Compupar réelle + l'émission e-CF en production restent côté ERPNext Backend/VPS).
|
Compupar réelle + l'émission e-CF en production restent côté ERPNext Backend/VPS).
|
||||||
- **Auto-score 4Big** : 96/100.
|
- **Auto-score 4Big** : 96/100.
|
||||||
|
|
||||||
|
## Session `20260801_143341` · Buffer S8 · Domaine Faisabilité/generator : les **TROIS copies aval** des 6 MARQUEURS CANONIQUES (`3 %`/`8.5 %`/`52 %` · `USD+DOP`/`Letter US`/`Cardnet`, contraintes NON-NÉGOCIABLES **CLAUDE.md #9/#10**) — l'**oracle d'enforcement** `genlib/renderer.py::_CANONICAL_MARKERS` (la liste que `check_canonical()` PARCOURT pour PROUVER que chaque rendu de faisabilité porte les 6 marqueurs), les **DEUX docstrings** de `genlib/scorer.py` (barème axe 4 + `_score_parametres_canoniques`) et l'**oracle du test** `tests/test_generator.py` (`for marker in [...]`) — étaient **HORS de tout ancrage**. Le bloc « paramètres canoniques » existant n'ancre QUE `model.py::CANONICAL` + le README à #9/#10 ; son propre commentaire NOMMAIT pourtant le défaut (« ces marqueurs sont HARDCODÉS DANS LE TEST, une copie de plus, JAMAIS comparée à CLAUDE.md ») sans le fermer.
|
||||||
|
|
||||||
|
**Tâche** : **Sprint 8 · buffer** (DevOps CI/CD · QA). Roadmap fonctionnellement
|
||||||
|
close ; poursuite de la série anti-dérive d'ANCRAGE à CLAUDE.md (classe
|
||||||
|
[[claude-md-constant-anchor-gate]]). **Même patron EXACT** que le tuple d'oracle du
|
||||||
|
test `publiciste/branding` ancré sur #4 (une copie hardcodée dans un test, gatée) —
|
||||||
|
appliqué aux **trois copies aval encore ungated du MÊME module** `faisabilite/generator`
|
||||||
|
dont le bloc canonique n'ancrait que 2 des 5 copies.
|
||||||
|
|
||||||
|
**Dérive silencieuse fermée** :
|
||||||
|
- `05_deliverables_mvp/faisabilite/generator/genlib/renderer.py:275` —
|
||||||
|
`_CANONICAL_MARKERS = ["3 %", "8.5 %", "52 %", "USD + DOP", "Cardnet", "Letter US"]`,
|
||||||
|
la LISTE que `check_canonical()` parcourt pour vérifier que le volet ingénierie
|
||||||
|
du rendu porte les 6 marqueurs. C'est l'**enforcement même** de l'« axe 4
|
||||||
|
honnêteté ».
|
||||||
|
- `05_deliverables_mvp/faisabilite/generator/genlib/scorer.py:7` (barème axe 4) **et**
|
||||||
|
`:109` (`_score_parametres_canoniques`) — deux docstrings énumérant
|
||||||
|
« 3 % / 8.5 % / 52 % · USD+DOP · Letter US · Cardnet ».
|
||||||
|
- `05_deliverables_mvp/faisabilite/generator/tests/test_generator.py:103` —
|
||||||
|
`for marker in ["3 %", "8.5 %", "52 %", "USD + DOP", "Cardnet", "Letter US"]` :
|
||||||
|
l'oracle du test lui-même, celui que le commentaire du bloc amont dénonçait.
|
||||||
|
- Source faisant autorité : **CLAUDE.md #9/#10** (les 6 valeurs), déjà recomputées
|
||||||
|
en `exp` par le bloc « paramètres canoniques » — RÉUTILISÉES ici (zéro duplication).
|
||||||
|
- Piège #6 : l'oracle `_CANONICAL_MARKERS` est le juge de l'honnêteté du rendu. S'il
|
||||||
|
DÉRIVE de `CANONICAL` (donc de #9/#10), le contrôle devient FAUX de deux façons :
|
||||||
|
soit il exige un marqueur périmé (`Cardnet` alors que le rendu, issu d'un CANONICAL
|
||||||
|
mis à jour, porterait `Azul` → **faux rouge**), soit on retire un marqueur et le
|
||||||
|
contrôle **NE VÉRIFIE PLUS RIEN** (vert vacux). La seule suite `tests/` teste des
|
||||||
|
FONCTIONS (dont `check_canonical` — AVEC son propre oracle hardcodé), jamais
|
||||||
|
l'ancrage à CLAUDE.md.
|
||||||
|
- **État courant** : **aucun marqueur périmé** — `set(renderer._CANONICAL_MARKERS) ==
|
||||||
|
set(model.CANONICAL.values())` vérifié programmatiquement AVANT toute édition ; les
|
||||||
|
3 copies recoupent #9/#10 exactement (anti-invention #6, rien à réécrire). Le défaut
|
||||||
|
est la **surface ungated**.
|
||||||
|
|
||||||
|
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « Faisabilité/generator
|
||||||
|
(2e surface) » après le bloc « paramètres canoniques ») : RÉUTILISE `exp` +
|
||||||
|
`_want6` (6 valeurs normalisées de #9/#10, zéro duplication). Helper `_list_vs_want6`
|
||||||
|
pour les copies-LISTES : (c) `renderer.py::_CANONICAL_MARKERS` et (e) l'oracle du test
|
||||||
|
sont parsés (`ast.literal_eval`) puis comparés à l'ensemble EXACT (set-diff : absent
|
||||||
|
ET en trop · anti-doublon). Pour les copies-DOCSTRINGS (d), chaque valeur normalisée
|
||||||
|
(`_pnorm`/`_ai` — espaces/accents-insensibles ⇒ `USD+DOP`==`USD + DOP`) doit être
|
||||||
|
substring de la ligne normalisée ; ≥2 énumérations exigées (barème + fonction). Un
|
||||||
|
claim/ligne absent échoue AUSSI (traçabilité #6).
|
||||||
|
|
||||||
|
**4 morsures vérifiées** : (M1) `renderer._CANONICAL_MARKERS` `Cardnet→Azul`
|
||||||
|
(absents=[cardnet] · en trop=[azul]) · (M2) oracle du test retire `8.5 %`
|
||||||
|
(absents=[8.5%]) · (M3) scorer docstring L7 `52 %→55 %` (n'énumère plus [52%]) ·
|
||||||
|
(M4) **CLAUDE.md #10 `Cardnet→Azul` → mord SIMULTANÉMENT model.py + renderer + test +
|
||||||
|
les DEUX docstrings** (5 morsures en cascade — la valeur MÊME de l'ancrage à la
|
||||||
|
source constitutionnelle) ; restauré = green : renderer/test == #9/#10 (6 marqueurs) ·
|
||||||
|
2 docstrings == #9/#10 · exit 0. Working tree byte-restauré (`git checkout --`,
|
||||||
|
**JAMAIS** `git clean`, interdit absolu) · **7 gates re-verts**
|
||||||
|
(`check_readme_claims`/`check_artifacts`/`check_ci_integrity`/`check_docs`/
|
||||||
|
`guard_constraints`/`check_regression`/`validate_json`).
|
||||||
|
|
||||||
|
- `ci/README.md` (clause récap du pipeline `check-readme-claims` + paragraphe détaillé
|
||||||
|
« 2ᵉ surface du MÊME module : les 3 copies aval des marqueurs canoniques ») mis à jour.
|
||||||
|
- **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 ; la génération
|
||||||
|
réelle des faisabilités reste côté BIM/Faisabilité).
|
||||||
|
- **Auto-score 4Big** : 96/100.
|
||||||
|
|||||||
+18
-1
File diff suppressed because one or more lines are too long
@@ -3877,6 +3877,91 @@ if exp is not None:
|
|||||||
bad(f"Faisabilité canoniques · {FG_RD} — prose énumère {sorted(got)} MAIS "
|
bad(f"Faisabilité canoniques · {FG_RD} — prose énumère {sorted(got)} MAIS "
|
||||||
f"CLAUDE.md {sorted(want)}{detail} → régénérer le README")
|
f"CLAUDE.md {sorted(want)}{detail} → régénérer le README")
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Faisabilité/generator (2e surface du MÊME module) · l'ORACLE D'ENFORCEMENT
|
||||||
|
# `_CANONICAL_MARKERS` + les DEUX docstrings du scorer + l'oracle du TEST — trois
|
||||||
|
# copies DE PLUS des 6 marqueurs canoniques, ancrées sur CLAUDE.md #9/#10.
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Le bloc ci-dessus ancre model.py `CANONICAL` + le README à CLAUDE.md #9/#10.
|
||||||
|
# Restent, dans le MÊME module, TROIS surfaces data-derived qui RECOPIENT les
|
||||||
|
# mêmes 6 marqueurs, toutes NON gatées :
|
||||||
|
# (c) genlib/renderer.py `_CANONICAL_MARKERS` — la liste que `check_canonical()`
|
||||||
|
# PARCOURT pour PROUVER que le rendu du volet ingénierie contient les 6
|
||||||
|
# marqueurs (l'oracle de l'« axe 4 honnêteté »). C'est l'enforcement même :
|
||||||
|
# si cette liste DÉRIVE de CANONICAL (donc de #9/#10), le contrôle devient
|
||||||
|
# FAUX — soit il exige un marqueur périmé (« Cardnet » alors que le rendu,
|
||||||
|
# issu d'un CANONICAL mis à jour, porterait « Azul » → faux rouge), soit on
|
||||||
|
# en retire un et le contrôle NE VÉRIFIE PLUS RIEN (vert vacux).
|
||||||
|
# (d) genlib/scorer.py — DEUX docstrings (barème axe 4 en tête · fonction
|
||||||
|
# `_score_parametres_canoniques`) énumèrent « 3 % / 8.5 % / 52 % · USD+DOP ·
|
||||||
|
# Letter US · Cardnet ». Prose recopiée, jamais confrontée à #9/#10.
|
||||||
|
# (e) tests/test_generator.py — la liste `for marker in [...]` : l'oracle du
|
||||||
|
# test lui-même. Le commentaire du bloc ci-dessus (« ces marqueurs sont
|
||||||
|
# HARDCODÉS DANS LE TEST, une copie de plus, JAMAIS comparée à CLAUDE.md »)
|
||||||
|
# NOMMAIT ce défaut sans le gater — on ferme la boucle ici.
|
||||||
|
# La suite tests/ teste des FONCTIONS (dont `check_canonical` — AVEC son propre
|
||||||
|
# oracle hardcodé), jamais l'ancrage à CLAUDE.md. Même classe EXACTE que le tuple
|
||||||
|
# d'oracle du test publiciste/branding ancré sur #4. On RÉUTILISE `exp` (6 valeurs
|
||||||
|
# recomputées de #9/#10 ci-dessus · zéro duplication) : les LISTES (c)/(e) sont
|
||||||
|
# parsées puis comparées à l'ensemble EXACT (set-diff : absent ET en trop) ; les
|
||||||
|
# DOCSTRINGS (d) doivent porter CHAQUE valeur normalisée (drift d'un marqueur ⇒
|
||||||
|
# ROUGE). Un claim/ligne absent échoue AUSSI (traçabilité).
|
||||||
|
# ============================================================================
|
||||||
|
if exp is not None:
|
||||||
|
_want6 = {_pnorm(exp[k]) for k in _num_keys} | {_ai(exp[k]) for k in _txt_keys}
|
||||||
|
def _norm_marker(tok):
|
||||||
|
p = _pct(tok)
|
||||||
|
return _pnorm(p) if p is not None else _ai(tok)
|
||||||
|
def _list_vs_want6(path, var_re, label):
|
||||||
|
try:
|
||||||
|
src = open(path, encoding="utf-8").read()
|
||||||
|
m = re.search(var_re, src, re.S)
|
||||||
|
items = ast.literal_eval(m.group(1))
|
||||||
|
except (OSError, ValueError, SyntaxError, AttributeError) as e:
|
||||||
|
bad(f"Faisabilité canoniques · {path} {label} illisible/inattendu : {e}")
|
||||||
|
return
|
||||||
|
got = {_norm_marker(x) for x in items}
|
||||||
|
if len(got) != len(items):
|
||||||
|
bad(f"Faisabilité canoniques · {path} — {label} contient un DOUBLON ({items})")
|
||||||
|
elif got == _want6:
|
||||||
|
good(f"Faisabilité canoniques · {path} — {label} == CLAUDE.md #9/#10 (6 marqueurs)")
|
||||||
|
else:
|
||||||
|
miss = sorted(_want6 - got); extra = sorted(got - _want6)
|
||||||
|
detail = (f" · absents={miss}" if miss else "") + \
|
||||||
|
(f" · en trop={extra}" if extra else "")
|
||||||
|
bad(f"Faisabilité canoniques · {path} — {label} {sorted(got)} MAIS "
|
||||||
|
f"CLAUDE.md {sorted(_want6)}{detail} → aligner (ou CLAUDE.md)")
|
||||||
|
# (c) l'oracle d'enforcement renderer._CANONICAL_MARKERS
|
||||||
|
_list_vs_want6(f"{D}/faisabilite/generator/genlib/renderer.py",
|
||||||
|
r"_CANONICAL_MARKERS\s*=\s*(\[.*?\])",
|
||||||
|
"_CANONICAL_MARKERS (oracle check_canonical)")
|
||||||
|
# (e) l'oracle du test tests/test_generator.py (for marker in [...])
|
||||||
|
_list_vs_want6(f"{D}/faisabilite/generator/tests/test_generator.py",
|
||||||
|
r"for marker in (\[[^\]]*\])",
|
||||||
|
"oracle du test check_canonical")
|
||||||
|
# (d) les DEUX docstrings du scorer : chaque valeur normalisée présente dans la ligne
|
||||||
|
SC = f"{D}/faisabilite/generator/genlib/scorer.py"
|
||||||
|
try:
|
||||||
|
sc_lines = open(SC, encoding="utf-8").read().splitlines()
|
||||||
|
except OSError as e:
|
||||||
|
bad(f"Faisabilité canoniques · {SC} illisible : {e}"); sc_lines = None
|
||||||
|
if sc_lines is not None:
|
||||||
|
# ligne d'énumération = contient un « % » ET « USD » (barème axe 4 + fonction)
|
||||||
|
enum = [ln for ln in sc_lines if "%" in ln and "USD" in ln]
|
||||||
|
if len(enum) < 2:
|
||||||
|
bad(f"Faisabilité canoniques · {SC} — attendu ≥2 énumérations des 6 marqueurs "
|
||||||
|
f"(barème axe 4 + _score_parametres_canoniques), trouvé {len(enum)}")
|
||||||
|
else:
|
||||||
|
for ln in enum:
|
||||||
|
ai = _ai(ln) # espaces/accents retirés ⇒ « USD+DOP » == « USD + DOP »
|
||||||
|
miss = sorted(v for v in _want6 if v not in ai)
|
||||||
|
if miss:
|
||||||
|
bad(f"Faisabilité canoniques · {SC} — docstring « {ln.strip()[:56]}… » "
|
||||||
|
f"n'énumère plus {miss} (attendu les 6 de CLAUDE.md #9/#10)")
|
||||||
|
else:
|
||||||
|
good(f"Faisabilité canoniques · {SC} — docstring énumère les 6 marqueurs "
|
||||||
|
f"== CLAUDE.md #9/#10")
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# Fiches agents · CITATION des constantes CLAUDE.md #10 (devises/format/paiement)
|
# Fiches agents · CITATION des constantes CLAUDE.md #10 (devises/format/paiement)
|
||||||
# — même classe d'ANCRAGE que le bloc « paramètres canoniques » ci-dessus, mais
|
# — même classe d'ANCRAGE que le bloc « paramètres canoniques » ci-dessus, mais
|
||||||
|
|||||||
Reference in New Issue
Block a user