[DTP-Worker 20260802_070524] Sprint 8 · buffer · Mobile app_config : gate d'IDENTITÉ du COMPTE D'INVARIANTS de _validate_bundle (8ᵉ instance de la classe ledger)
Recompute le compte « 12 invariants » depuis le SEUL registre-source (`# Invariant N ·` de `_validate_bundle`) — 4 surfaces prose hand-transcrites (CLI .py, 2× README, docstring test) ancrées, aucune n'était gatée. Style de marqueur `# Invariant N ·` + fonction `_validate_bundle` = deux différences structurelles vs les 7 blocs `# N ·`/`_validate` précédents ; ajoute la surface fichier-de-tests (b'). 4 verts arbre propre · 8 morsures adversariales · 6 gates CI exit 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -718,3 +718,61 @@ CODE, pas la prose) · M5 suppression `# 7 ·` ⇒ gap non-contigu (a) · M6 dou
|
|||||||
7 (e) · M8 suppression `validate(bundle, schema)` ⇒ invariant 1 introuvable. Gates
|
7 (e) · M8 suppression `validate(bundle, schema)` ⇒ invariant 1 introuvable. Gates
|
||||||
re-verts : `check_artifacts` · `check_docs` · `guard_constraints` ·
|
re-verts : `check_artifacts` · `check_docs` · `guard_constraints` ·
|
||||||
`check_ci_integrity` · `check_readme_claims` · `check_regression` tous exit 0.
|
`check_ci_integrity` · `check_readme_claims` · `check_regression` tous exit 0.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sprint 8 · buffer · Mobile app_config : gate d'IDENTITÉ du COMPTE D'INVARIANTS de `_validate_bundle` (8ᵉ instance)
|
||||||
|
|
||||||
|
**Contexte** — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention de
|
||||||
|
chiffres »). La classe « ledger d'invariants » (recompute le nombre d'invariants CI
|
||||||
|
depuis le registre-source numéroté de la fonction de validation, jamais une liste à
|
||||||
|
la main) était déjà appliquée à **7 générateurs** : Chat OTOIA · SEO · Fiscal e-CF ·
|
||||||
|
Legal CONFOTUR · CRM Dossier Vente · QA Audit 5D · CRM Commissions. Le **8ᵉ**
|
||||||
|
générateur portant le même nombre magique restait NON couvert :
|
||||||
|
`mobile/app_config/app_config_gen.py`.
|
||||||
|
|
||||||
|
Le compte **« 12 invariants »** y est transcrit **à la main à QUATRE endroits**,
|
||||||
|
aucun gaté, tous invisibles à `check_artifacts` (le `.py`/`.md`/test n'est pas un
|
||||||
|
`out/*.json`) :
|
||||||
|
1. `app_config_gen.py` message de succès CLI « … schéma + 12 invariants verts. »
|
||||||
|
(l.228) ;
|
||||||
|
2. `README.md` bloc de commande « schéma + 12 invariants » (l.48) ;
|
||||||
|
3. `README.md` prose de récap « Schéma + 12\ninvariants verts » (l.67-68 — le « 12 »
|
||||||
|
et le mot séparés par un saut de ligne) ;
|
||||||
|
4. `tests/test_mobile_app_config.py` docstring « … + les 12 invariants du CLI »
|
||||||
|
(l.4).
|
||||||
|
|
||||||
|
Ajouter un contrôle `# Invariant 13 ·` à `_validate_bundle` sans toucher les 4
|
||||||
|
chaînes ⇒ le README, le self-report CLI ET la docstring de test mentent en silence
|
||||||
|
pendant que la CI applique 13 contrôles. Aucune suite `tests/` (qui EXÉCUTE la
|
||||||
|
validation mais ne compte jamais la prose) ne l'attrape.
|
||||||
|
|
||||||
|
**Particularités vs les 7 blocs précédents** (documentées dans l'en-tête du gate) :
|
||||||
|
- le registre-source utilise le style **`# Invariant N ·`** (le mot « Invariant »
|
||||||
|
précède le numéro) — les blocs ancrés sur le style nu `# N ·` NE le voient PAS ;
|
||||||
|
ancrage sur `#\s*Invariant\s+(\d+)…` ;
|
||||||
|
- la fonction s'appelle **`_validate_bundle`** (pas `_validate`) — l'ancre `def
|
||||||
|
_validate\b` des autres blocs échoue (`\b` échoue avant `_bundle`) ;
|
||||||
|
- ce README n'a NI titre « ## Les N invariants » NI énumération numérotée → on gate
|
||||||
|
**(a)(b)(b')(d)**, en AJOUTANT la surface **fichier-de-tests (b')**, absente des 7
|
||||||
|
blocs précédents (leurs tests ne portaient pas le chiffre).
|
||||||
|
|
||||||
|
**Gate ajouté** (`ci/check_readme_claims.sh`, bloc « Mobile · app_config · IDENTITÉ
|
||||||
|
du COMPTE D'INVARIANTS ») — RECOMPUTE le ledger depuis le SEUL registre-source
|
||||||
|
(`# Invariant N ·` de `_validate_bundle`) et exige :
|
||||||
|
- **(a)** ledger CONTIGU 1..N (schéma compté à part ; lacune OU doublon nu = défaut
|
||||||
|
mordu) ;
|
||||||
|
- **(b)** toute mention « N invariants » du `.py` == |ledger| ;
|
||||||
|
- **(b')** toute mention « N invariants » du fichier de tests == |ledger| ;
|
||||||
|
- **(d)** toute mention « N invariants » du README == |ledger|.
|
||||||
|
|
||||||
|
**Vérif** — 4 checks verts sur l'arbre propre (ledger 1..12 = 12 marqueurs). **8
|
||||||
|
morsures** adversariales : M1 self-report CLI 12→13 (b) · M2 README bloc-commande
|
||||||
|
12→13 (d) · M3 README récap (newline-split) 12→13 (d, preuve que `\s+` traverse le
|
||||||
|
saut de ligne) · M4 docstring test 12→13 (b', la surface neuve) · M5 ajout marqueur
|
||||||
|
`# Invariant 13 ·` ⇒ ledger→13 pendant que les 3 surfaces prose restent 12 (b+b'+d,
|
||||||
|
preuve que l'autorité est le CODE, pas la prose) · M6 suppression `# Invariant 7 ·`
|
||||||
|
⇒ gap non-contigu (a) + désync prose · M7 doublon nu `# Invariant 5 ·` ⇒ détection
|
||||||
|
de vrai doublon · M8 renommage `_validate_bundle` ⇒ registre introuvable. Gates
|
||||||
|
re-verts : `check_artifacts` · `check_docs` · `guard_constraints` ·
|
||||||
|
`check_ci_integrity` · `check_readme_claims` · `check_regression` tous exit 0.
|
||||||
|
|||||||
@@ -8523,6 +8523,129 @@ if cmi_src is not None:
|
|||||||
bad(f"Commissions invariants · {CMI_RD} — énumération prose {cmi_enum} "
|
bad(f"Commissions invariants · {CMI_RD} — énumération prose {cmi_enum} "
|
||||||
f"≠ 1..{cmi_ninv} (item sauté/dupliqué) → régénérer le README")
|
f"≠ 1..{cmi_ninv} (item sauté/dupliqué) → régénérer le README")
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Mobile · app_config · IDENTITÉ du COMPTE D'INVARIANTS de `_validate_bundle` —
|
||||||
|
# 8ᵉ instance de la classe « ledger d'invariants » (après Chat OTOIA · SEO ·
|
||||||
|
# Fiscal e-CF · Legal CONFOTUR · CRM Dossier Vente · QA Audit 5D · CRM
|
||||||
|
# Commissions), appliquée au générateur de la config app mobile (roadmap Sprint 5
|
||||||
|
# l.56-57 « Rebuild Expo 54 + submit »).
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Le générateur app_config_gen.py applique un COMPTE d'invariants de cohérence
|
||||||
|
# (onglets=portails RBAC · rôles=surface exacte · marque #4 · devises #10 · Expo
|
||||||
|
# SDK · anti-invention a_confirmer · déterminisme…) dont la source d'autorité
|
||||||
|
# n'est PAS un artefact JSON (invisible à check_artifacts qui byte-gate les
|
||||||
|
# out/*.json) mais le LEDGER NUMÉROTÉ de `_validate_bundle` : les marqueurs
|
||||||
|
# `# Invariant N ·` (1..12) qui ouvrent chaque contrôle. Le nombre « 12 » est
|
||||||
|
# transcrit À LA MAIN à QUATRE endroits qu'AUCUN gate ne couvrait : UNE chaîne du
|
||||||
|
# .py (message de succès CLI « … schéma + 12 invariants verts »), DEUX du README
|
||||||
|
# (bloc de commande « schéma + 12 invariants », prose de récap « Schéma + 12
|
||||||
|
# invariants verts » — le « 12 » et le mot séparés par un saut de ligne) + UNE du
|
||||||
|
# fichier de tests (docstring « … + les 12 invariants du CLI »). Ajouter un
|
||||||
|
# contrôle `# Invariant 13 ·` à `_validate_bundle` sans toucher ces chaînes ⇒ le
|
||||||
|
# README, le self-report CLI ET la docstring de test mentent en silence pendant
|
||||||
|
# que la CI applique 13 contrôles — « vert trompeur » (#6) que ni les suites
|
||||||
|
# tests/ (qui EXÉCUTENT la validation mais ne comptent jamais la prose) ni le
|
||||||
|
# byte-gate d'artefact (aveugle au .py source) n'attrapent.
|
||||||
|
# PARTICULARITÉS vs les 7 blocs précédents :
|
||||||
|
# · le REGISTRE-source utilise le style `# Invariant N ·` (le mot « Invariant »
|
||||||
|
# précède le numéro) — les blocs Chat OTOIA/SEO/Fiscal/CONFOTUR/Dossier/Audit
|
||||||
|
# 5D/Commissions ancrés sur le style nu `# N ·` NE le voient PAS ; on ancre
|
||||||
|
# donc sur `#\s*Invariant\s+(\d+)…` ;
|
||||||
|
# · la fonction s'appelle `_validate_bundle` (pas `_validate`) — l'ancre `def
|
||||||
|
# _validate\b` des autres blocs ne matche pas (`\b` échoue avant `_bundle`) ;
|
||||||
|
# · ledger PROPRE 1..N compté À PART du schéma (phrasé « schéma + N invariants »)
|
||||||
|
# — pas de schéma-#1 non marqué, pas de split a/b ; on garde le filet doublon
|
||||||
|
# nu ;
|
||||||
|
# · ce README n'a NI titre « ## Les N invariants » NI énumération numérotée du
|
||||||
|
# ledger → on gate (a)(b)(b')(d), en AJOUTANT la surface fichier-de-tests (b')
|
||||||
|
# absente des blocs précédents (leurs tests ne portaient pas le chiffre).
|
||||||
|
# On RECOMPUTE le ledger depuis le SEUL registre-source (les `# Invariant N ·` de
|
||||||
|
# `_validate_bundle`) et on exige : (a) ledger CONTIGU 1..N (lacune OU doublon nu
|
||||||
|
# = vrai défaut de registre, mordu) ; (b) TOUTE mention « N invariants » du .py ==
|
||||||
|
# |ledger| ; (b') TOUTE mention « N invariants » du fichier de tests == |ledger| ;
|
||||||
|
# (d) TOUTE mention « N invariants » du README == |ledger|. Mention ou registre
|
||||||
|
# absent = échec (disparition d'un chiffre = régression de traçabilité #6).
|
||||||
|
# ============================================================================
|
||||||
|
MBI_GEN = f"{D}/mobile/app_config/app_config_gen.py"
|
||||||
|
MBI_RD = f"{D}/mobile/app_config/README.md"
|
||||||
|
MBI_TS = f"{D}/mobile/app_config/tests/test_mobile_app_config.py"
|
||||||
|
try:
|
||||||
|
mbi_src = open(MBI_GEN, encoding="utf-8").read()
|
||||||
|
except OSError as e:
|
||||||
|
bad(f"Mobile invariants · générateur {MBI_GEN} illisible : {e}"); mbi_src = None
|
||||||
|
if mbi_src is not None:
|
||||||
|
_mfn = re.search(r"def _validate_bundle\b", mbi_src)
|
||||||
|
if _mfn is None:
|
||||||
|
bad(f"Mobile invariants · fonction `_validate_bundle` INTROUVABLE dans {MBI_GEN} "
|
||||||
|
"(registre-source du ledger absent → impossible de re-dériver)")
|
||||||
|
else:
|
||||||
|
_bs = _mfn.end()
|
||||||
|
_mn = re.search(r"\ndef ", mbi_src[_bs:])
|
||||||
|
_body = mbi_src[_bs: _bs + _mn.start()] if _mn else mbi_src[_bs:]
|
||||||
|
_pairs = re.findall(r"#\s*Invariant\s+(\d+)([a-z]?)\s*·", _body)
|
||||||
|
_dup_bare = sorted({n for n in {p[0] for p in _pairs}
|
||||||
|
if [s for num, s in _pairs if num == n and s == ""]
|
||||||
|
and sum(1 for num, _ in _pairs if num == n) > 1})
|
||||||
|
_top = sorted({int(n) for n, _ in _pairs})
|
||||||
|
if not _top:
|
||||||
|
bad(f"Mobile invariants · aucun marqueur `# Invariant N ·` dans `_validate_bundle` "
|
||||||
|
f"de {MBI_GEN} (ledger vide → impossible de re-dériver)")
|
||||||
|
elif _dup_bare:
|
||||||
|
bad(f"Mobile invariants · marqueur(s) `# Invariant N ·` dupliqué(s) sans suffixe "
|
||||||
|
f"a/b dans `_validate_bundle` : {_dup_bare} — vrai doublon de registre")
|
||||||
|
else:
|
||||||
|
mbi_ninv = len(_top)
|
||||||
|
if _top != list(range(1, mbi_ninv + 1)):
|
||||||
|
bad(f"Mobile invariants · ledger `_validate_bundle` NON contigu : {_top} "
|
||||||
|
f"(attendu 1..{mbi_ninv}) — lacune/doublon dans les marqueurs `# Invariant N ·`")
|
||||||
|
else:
|
||||||
|
good(f"Mobile invariants · ledger `_validate_bundle` contigu 1..{mbi_ninv} "
|
||||||
|
f"({mbi_ninv} marqueurs `# Invariant N ·` · schéma compté à part)")
|
||||||
|
# (b) self-report(s) du .py « N invariants ».
|
||||||
|
mbi_py_rep = sorted({int(x) for x in re.findall(r"(\d+)\s+invariants", mbi_src)})
|
||||||
|
if not mbi_py_rep:
|
||||||
|
bad(f"Mobile invariants · aucune mention « N invariants » dans {MBI_GEN} "
|
||||||
|
f"(self-report absent → attendu {mbi_ninv})")
|
||||||
|
elif mbi_py_rep == [mbi_ninv]:
|
||||||
|
good(f"Mobile invariants · {MBI_GEN} — self-report(s) {mbi_py_rep} "
|
||||||
|
f"== ledger ({mbi_ninv})")
|
||||||
|
else:
|
||||||
|
bad(f"Mobile invariants · {MBI_GEN} — self-report(s) « N invariants » "
|
||||||
|
f"{mbi_py_rep} MAIS ledger en compte {mbi_ninv} "
|
||||||
|
"(une chaîne du générateur désynchronisée du registre)")
|
||||||
|
# (b') self-report du fichier de tests « N invariants » (docstring).
|
||||||
|
try:
|
||||||
|
mbi_ts_raw = open(MBI_TS, encoding="utf-8").read()
|
||||||
|
except OSError as e:
|
||||||
|
bad(f"Mobile invariants · {MBI_TS} illisible : {e}"); mbi_ts_raw = None
|
||||||
|
if mbi_ts_raw is not None:
|
||||||
|
mbi_ts_rep = sorted({int(x) for x in re.findall(r"(\d+)\s+invariants", mbi_ts_raw)})
|
||||||
|
if not mbi_ts_rep:
|
||||||
|
bad(f"Mobile invariants · aucune mention « N invariants » dans {MBI_TS} "
|
||||||
|
f"(self-report tests absent → attendu {mbi_ninv})")
|
||||||
|
elif mbi_ts_rep == [mbi_ninv]:
|
||||||
|
good(f"Mobile invariants · {MBI_TS} — docstring {mbi_ts_rep} "
|
||||||
|
f"== ledger ({mbi_ninv})")
|
||||||
|
else:
|
||||||
|
bad(f"Mobile invariants · {MBI_TS} — docstring « N invariants » "
|
||||||
|
f"{mbi_ts_rep} MAIS ledger en compte {mbi_ninv} → régénérer la docstring")
|
||||||
|
# (d) toute mention « N invariants » du README == ledger.
|
||||||
|
try:
|
||||||
|
mbi_rd_raw = open(MBI_RD, encoding="utf-8").read()
|
||||||
|
except OSError as e:
|
||||||
|
bad(f"Mobile invariants · {MBI_RD} illisible : {e}"); mbi_rd_raw = None
|
||||||
|
if mbi_rd_raw is not None:
|
||||||
|
mbi_rd_rep = sorted({int(x) for x in re.findall(r"(\d+)\s+invariants", mbi_rd_raw)})
|
||||||
|
if not mbi_rd_rep:
|
||||||
|
bad(f"Mobile invariants · aucune mention « N invariants » dans {MBI_RD} "
|
||||||
|
f"(self-report README absent → attendu {mbi_ninv})")
|
||||||
|
elif mbi_rd_rep == [mbi_ninv]:
|
||||||
|
good(f"Mobile invariants · {MBI_RD} — mention(s) {mbi_rd_rep} "
|
||||||
|
f"== ledger (aucune dérive)")
|
||||||
|
else:
|
||||||
|
bad(f"Mobile invariants · {MBI_RD} — mention(s) « N invariants » "
|
||||||
|
f"{mbi_rd_rep} MAIS ledger en compte {mbi_ninv} → régénérer le README")
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# Traçabilité roadmap · le `roadmap_ref` VERBATIM des artefacts — la CITATION
|
# Traçabilité roadmap · le `roadmap_ref` VERBATIM des artefacts — la CITATION
|
||||||
# (n° de ligne + « libellé bullet ») ANCRÉE sur la ligne RÉELLE de la roadmap,
|
# (n° de ligne + « libellé bullet ») ANCRÉE sur la ligne RÉELLE de la roadmap,
|
||||||
|
|||||||
Reference in New Issue
Block a user