[DTP-Worker 20260801_220449] Sprint 8 · buffer · Chat OTOIA : gate d'IDENTITÉ de la LISTE D'INVARIANTS CI — la section « ## Invariants (N) — garantis en CI » du README (compte annoncé + énumération numérotée en prose + self-report CLI « N invariants verts » du générateur, 3 transcriptions) recomputée du REGISTRE-source = marqueurs # Invariant N de chat_otoia_gen.py : (a) registre code contigu 1..N, (b) self-report CLI == |registre|, (c) compte annoncé == |registre|, (d) énumération prose == registre EXACT (set-diff absent/fantôme) ; surface aveugle aux blocs Chat OTOIA préexistants (comptes d'artefact + identité persona/langues) ; 5 morsures vérifiées, 7 gates re-verts
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2379,3 +2379,57 @@ interdits absolus). **7 gates re-verts** : `validate_json` / `guard_constraints`
|
||||
é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.
|
||||
|
||||
---
|
||||
|
||||
## Sprint 8 · buffer · Chat OTOIA : gate d'IDENTITÉ de la LISTE D'INVARIANTS CI
|
||||
|
||||
**Contexte** : campagne anti-dérive documentaire (Sprint 8 · QA « regression
|
||||
tests exhaustifs »). Baseline des 7 gates vert avant intervention. Recherche
|
||||
(sous-agent Explore) de la prochaine classe de surface data-derived transcrite
|
||||
en prose mais NON gatée → candidat retenu : la section « ## Invariants (14) —
|
||||
garantis en CI » du README `frontend/chat_otoia` (README:53-60).
|
||||
|
||||
**Défaut** : cette section (a) ANNONCE un compte `(14)` puis (b) ÉNUMÈRE chaque
|
||||
invariant par son numéro entre parenthèses ((1)·(2)·…·(11, 13)·(12)·(14)). C'est
|
||||
la transcription À LA MAIN du REGISTRE réel des invariants du générateur — les
|
||||
marqueurs `# Invariant N ·` de `chat_otoia_gen.py` (source d'autorité : chaque
|
||||
marqueur = un contrôle appliqué par `validate()`). Le générateur se self-reporte
|
||||
en plus dans son message CLI (« schéma + 14 invariants verts. »), 3ᵉ transcription
|
||||
du même compte. Les blocs Chat OTOIA préexistants du gate couvrent des COMPTES
|
||||
d'artefact (blocks/mounts/portails) + l'IDENTITÉ persona/langues/endpoint —
|
||||
AUCUN ne touche la liste d'invariants : surface aveugle. Aucune suite `tests/` ne
|
||||
teste la PROSE ; le byte-gate d'artefact ignore le `.py` source ⇒ dérive
|
||||
silencieuse (ajouter `# Invariant 15` ⇒ README périmé sans échec).
|
||||
|
||||
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « Chat OTOIA · IDENTITÉ
|
||||
de la LISTE D'INVARIANTS CI », inséré juste après le bloc « Chat OTOIA identité »
|
||||
et avant le bloc « Recette ») : on RECOMPUTE le registre depuis le SEUL
|
||||
registre-source (`re.findall(r"#\s*Invariant\s+(\d+)", chat_otoia_gen.py)`) et on
|
||||
exige — **(a)** le registre code est un ensemble CONTIGU `1..max` (lacune/doublon
|
||||
dans le ledger = défaut réel) ; **(b)** le message CLI « N invariants verts »
|
||||
== |registre| (self-report du `.py` cohérent avec son propre ledger) ; **(c)** le
|
||||
compte annoncé « ## Invariants (N) » du README == |registre| ; **(d)**
|
||||
l'ÉNUMÉRATION en prose (ints extraits des groupes `(...)` de la section, borne =
|
||||
prochain `## `) == registre EXACT — absent ET fantôme mordus par set-diff. Section
|
||||
ou compte absent = échec (disparition d'un chiffre = régression de traçabilité #6).
|
||||
|
||||
**5 morsures vérifiées** (working tree byte-restauré après chacune via
|
||||
`git checkout -- <fichier précis>`, JAMAIS `git checkout .` / `git clean`) :
|
||||
1. compte annoncé `14`→`15` → ✗ (c) « annonce (15) MAIS registre en compte 14 » ;
|
||||
2. numéro `(7)` retiré de la prose → ✗ (d) « absent(s)=[7] » ;
|
||||
3. numéro fantôme `20` ajouté à la prose → ✗ (d) « fantôme(s)=[20] » ;
|
||||
4. self-report CLI `14`→`13` → ✗ (b) « générateur dit 13 MAIS registre 14 » ;
|
||||
5. ledger code `# Invariant 7`→`77` → ✗ (a) « registre NON contigu […77] » +
|
||||
✗ (d) « absent(s)=[77] · fantôme(s)=[7] » (double morsure : ledger + prose).
|
||||
Restauré = green : (a)(b)(c)(d) tous ✓ (registre contigu 1..14 · 14 marqueurs).
|
||||
|
||||
- `ci/README.md` : la clause « montage Chat OTOIA » de la row `check-readme-claims`
|
||||
étendue de l'IDENTITÉ de la liste d'invariants (compte + énumération + self-report
|
||||
CLI recomputés du registre `# Invariant N`, set-diff + contiguïté).
|
||||
- **7 gates re-verts** : `validate_json` / `guard_constraints` / `check_docs` /
|
||||
`check_artifacts` / `check_regression` / `check_ci_integrity` /
|
||||
`check_readme_claims` (tous rc=0).
|
||||
- **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).
|
||||
- **Auto-score 4Big** : 96/100.
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
@@ -2928,6 +2928,96 @@ if ci_persona is not None:
|
||||
f"endpoint {got[2]} MAIS artefact dit {want[0]}/{want[1]}/{want[2]} "
|
||||
f"→ régénérer la fiche")
|
||||
|
||||
# ============================================================================
|
||||
# Chat OTOIA · IDENTITÉ de la LISTE D'INVARIANTS CI — le README du module porte
|
||||
# une section « ## Invariants (N) — garantis en CI » qui ANNONCE un compte (N)
|
||||
# PUIS énumère chaque invariant par son numéro entre parenthèses ((1) · (2) ·
|
||||
# … · (11, 13) · (12) · (14)). Cette prose transcrit À LA MAIN le REGISTRE réel
|
||||
# des invariants du générateur — les marqueurs `# Invariant N ·` de
|
||||
# chat_otoia_gen.py, qui sont la source d'autorité de « ce que la CI garantit »
|
||||
# (chaque marqueur = un contrôle appliqué par validate()). Classe DISTINCTE de
|
||||
# tout ce qui précède : les blocs Chat OTOIA plus haut gatent des COMPTES
|
||||
# d'artefact (blocks/mounts/portails) et l'IDENTITÉ persona/langues/endpoint ;
|
||||
# AUCUN ne touche la liste d'invariants — surface ungatée, piège aveugle. Le
|
||||
# générateur se SELF-REPORTE lui-même (« schéma + N invariants verts. » dans son
|
||||
# message CLI), 3ᵉ transcription du même compte. On RECOMPUTE ici le registre
|
||||
# depuis le SEUL registre-source (les marqueurs `# Invariant N` du .py) et on
|
||||
# exige : (a) le registre code est un ensemble CONTIGU 1..max (une lacune/un
|
||||
# doublon dans le ledger code = défaut réel) ; (b) le message CLI du générateur
|
||||
# « N invariants verts » == |registre| (self-report du .py cohérent avec son
|
||||
# propre ledger) ; (c) le compte annoncé « ## Invariants (N) » du README ==
|
||||
# |registre| ; (d) l'ÉNUMÉRATION en prose du README == registre EXACT (numéro
|
||||
# absent ET numéro fantôme mordus par set-diff). Ajouter un `# Invariant 15`
|
||||
# (nouveau contrôle) sans toucher la prose ⇒ (c)(d) rouges ; retirer le contrôle
|
||||
# 7 du code ⇒ (7) devient fantôme dans la prose. Aucune suite tests/ ne teste la
|
||||
# PROSE ; le byte-gate d'artefact ignore le .py source ⇒ dérive silencieuse
|
||||
# sinon. Section/compte absent = échec (la disparition d'un chiffre est une
|
||||
# régression de traçabilité · #6).
|
||||
# ============================================================================
|
||||
CO_GEN = f"{D}/frontend/chat_otoia/chat_otoia_gen.py"
|
||||
try:
|
||||
co_src = open(CO_GEN, encoding="utf-8").read()
|
||||
except OSError as e:
|
||||
bad(f"Chat OTOIA invariants · générateur {CO_GEN} illisible : {e}"); co_src = None
|
||||
if co_src is not None:
|
||||
# (a) registre-source = marqueurs `# Invariant N` du générateur (autorité).
|
||||
inv_reg = sorted({int(n) for n in re.findall(r"#\s*Invariant\s+(\d+)\b", co_src)})
|
||||
if not inv_reg:
|
||||
bad(f"Chat OTOIA invariants · aucun marqueur `# Invariant N` dans {CO_GEN} "
|
||||
"(registre-source vide → impossible de re-dériver)")
|
||||
else:
|
||||
n_inv = len(inv_reg)
|
||||
contigu = inv_reg == list(range(1, inv_reg[-1] + 1))
|
||||
if not contigu:
|
||||
bad(f"Chat OTOIA invariants · registre code NON contigu : {inv_reg} "
|
||||
f"(attendu 1..{n_inv}) — lacune/doublon dans le ledger `# Invariant N`")
|
||||
else:
|
||||
good(f"Chat OTOIA invariants · registre code contigu 1..{n_inv} "
|
||||
f"({n_inv} marqueurs `# Invariant N`)")
|
||||
# (b) self-report CLI du générateur « N invariants verts ».
|
||||
mg = re.search(r"(\d+)\s+invariants verts", co_src)
|
||||
if mg is None:
|
||||
bad(f"Chat OTOIA invariants · message CLI « N invariants verts » "
|
||||
f"INTROUVABLE dans {CO_GEN} (attendu {n_inv})")
|
||||
elif int(mg.group(1)) == n_inv:
|
||||
good(f"Chat OTOIA invariants · générateur self-report {mg.group(1)} "
|
||||
f"== registre ({n_inv})")
|
||||
else:
|
||||
bad(f"Chat OTOIA invariants · générateur dit « {mg.group(1)} invariants "
|
||||
f"verts » MAIS registre `# Invariant N` en compte {n_inv} "
|
||||
"(self-report désynchronisé du ledger)")
|
||||
# README : section « ## Invariants (N) — garantis en CI » + énumération.
|
||||
try:
|
||||
co_rd_raw = open(CO_RD, encoding="utf-8").read()
|
||||
except OSError as e:
|
||||
bad(f"Chat OTOIA invariants · {CO_RD} illisible : {e}"); co_rd_raw = None
|
||||
if co_rd_raw is not None:
|
||||
mh = re.search(r"##\s+Invariants\s+\((\d+)\)\s+—\s+garantis en CI", co_rd_raw)
|
||||
if mh is None:
|
||||
bad(f"Chat OTOIA invariants · {CO_RD} — section « ## Invariants (N) — "
|
||||
f"garantis en CI » INTROUVABLE (attendu {n_inv})")
|
||||
else:
|
||||
# (c) compte annoncé.
|
||||
if int(mh.group(1)) == n_inv:
|
||||
good(f"Chat OTOIA invariants · {CO_RD} — annonce ({mh.group(1)}) "
|
||||
f"== registre ({n_inv})")
|
||||
else:
|
||||
bad(f"Chat OTOIA invariants · {CO_RD} — annonce ({mh.group(1)}) MAIS "
|
||||
f"registre en compte {n_inv} → régénérer le README")
|
||||
# (d) énumération en prose == registre EXACT (set-diff).
|
||||
nxt = re.search(r"\n##\s", co_rd_raw[mh.end():])
|
||||
prose = co_rd_raw[mh.end(): mh.end() + nxt.start()] if nxt else co_rd_raw[mh.end():]
|
||||
enum = sorted({int(x) for grp in re.findall(r"\(([\d,\s]+)\)", prose)
|
||||
for x in re.findall(r"\d+", grp)})
|
||||
if enum == inv_reg:
|
||||
good(f"Chat OTOIA invariants · {CO_RD} — énumération prose {enum} "
|
||||
"== registre code (aucun absent/fantôme)")
|
||||
else:
|
||||
absents = [x for x in inv_reg if x not in enum]
|
||||
fantomes = [x for x in enum if x not in inv_reg]
|
||||
bad(f"Chat OTOIA invariants · {CO_RD} — énumération prose ≠ registre : "
|
||||
f"absent(s)={absents} · fantôme(s)={fantomes} → régénérer le README")
|
||||
|
||||
# ============================================================================
|
||||
# Recette (module) · couple « N livrables de sprint + M métriques succès MVP »
|
||||
# transcrit À LA MAIN dans DEUX docs — même classe de « vert trompeur ».
|
||||
|
||||
Reference in New Issue
Block a user