[DTP-Worker] Sprint 8 · buffer L75 · Domaine Mobile (2e surface) : la ligne récap du README de l'app Expo ÉNUMÉRAIT À LA MAIN ses 5 onglets « (Ventes/Construction/Achat/Compta/Direction) » sans AUCUN gate d'IDENTITÉ. Le bloc mobile existant recompute le QUADRUPLET de la ligne (onglets · rôles couverts · langues · identifiants a_confirmer) depuis MANIFEST.counts, MAIS son patron du compte matche la parenthèse par \([^)]*\) et en IGNORE le contenu — surface data-derived distincte du MÊME README, même ligne. Cette liste dérive de mobile/app_config/out/role_navigation.json (byte-gaté par check_artifacts) : un onglet par portail métier du contrat RBAC (bijection 1 onglet ⇔ 1 portail, plateforme exclu), chaque entrée = son label « OTO <Nom> » ; nom court énuméré = label sans le préfixe « OTO ». Le bloc mobile ne gate QUE le COMPTE (« 5 onglets ») : un ÉCHANGE (Ventes→Marketing) laisse le compte à 5 — le compteur reste AVEUGLE — pendant que l'artefact dit autre chose ; un RETRAIT (Compta absent) fait tomber l'énumération à 4 tandis que « 5 onglets » reste juste ; un RÉORDRE passe aussi — « vert trompeur » qu'aucune suite tests/ (qui teste des FONCTIONS, pas la prose) n'attrape → nouveau bloc dans check_readme_claims recomputant la LISTE ORDONNÉE des noms courts depuis role_navigation (zéro duplication de la navigation du générateur #6) et exigeant que la parenthèse l'énumère EXACTEMENT. Contrôle par ENSEMBLE (absent ET en trop mordus via set-diff), puis ORDRE. Même patron que l'énumération des confirmations DevOps, la carte de renormalisation par archétype 4Big ou la table « Verdict courant » de l'audit 5D. Cohérence croisée en bonus : len(role_navigation) == MANIFEST.counts.onglets == MANIFEST.counts.portails (la bijection du générateur) — mord un compteur du manifeste désynchronisé de sa navigation. Un claim absent échoue AUSSI (4 morsures vérifiées : échange Ventes→Marketing capté là où le compte reste 5 · retrait Compta capté là où « 5 onglets » reste juste · réordre capté « même ensemble, ordre ≠ » · énumération supprimée = INTROUVABLE ; restauré = green : Ventes/Construction/Achat/Compta/Direction == role_navigation · bijection 5==onglets==portails). État courant : aucun onglet périmé (anti-invention #6, rien à réécrire) — le défaut est la surface ungated. ci/README.md (table + détail) mis à jour · 7 gates re-verts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Code DTP Worker
2026-07-31 15:07:06 +00:00
parent 6a40bfccce
commit c5ac020270
3 changed files with 135 additions and 2 deletions
+60
View File
@@ -1,5 +1,65 @@
# Activity Log · 2026-07-31 · Claude Code DTP # Activity Log · 2026-07-31 · Claude Code DTP
## Session `20260731_150110` · Buffer S8 · Domaine Mobile (2e surface) : la ligne récap du README app Expo **énumérait** À LA MAIN ses **5 onglets** (`(Ventes/Construction/Achat/Compta/Direction)`) sans gate d'IDENTITÉ — le bloc mobile existant ne recompute que le COMPTE (« 5 onglets ») et son patron matche la parenthèse par `\([^)]*\)` en **ignorant son contenu**, aveugle à un **échange / retrait / réordre** de nom.
**Tâche** : **Sprint 8 · buffer L75** (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive (CLAUDE.md #6). Le bloc `mobile` gaté
précédemment recompute le **quadruplet** de la ligne récap (`onglets` · `rôles`
couverts · `langues` · `identifiants a_confirmer`) depuis `MANIFEST.counts`. Restait,
dans la **même ligne du même README**, une surface data-derived distincte : la
**parenthèse qui énumère les 5 onglets** — matchée par le patron du compte mais
dont le **contenu est ignoré**.
**Dérive silencieuse fermée** :
- `05_deliverables_mvp/mobile/app_config/README.md:66` — « **5 onglets**
(Ventes/Construction/Achat/Compta/Direction) · … ». La parenthèse est une **liste
ordonnée data-derived** ; le patron du compte (`\*\*(\d+) onglets\*\* \([^)]*\)`)
la matche mais IGNORE son contenu.
- Source faisant autorité : `mobile/app_config/out/role_navigation.json` (byte-gaté
par `check_artifacts`) — un onglet par portail métier du contrat RBAC (bijection
1 onglet ⇔ 1 portail · `plateforme` exclu), chaque entrée portant `portail` (id)
+ `label` « OTO <Nom> ». Nom court énuméré = le `label` sans le préfixe « OTO ».
- Le bloc mobile ne gate QUE le **compte** (« 5 onglets »). Un **échange**
(`Ventes → Marketing`), un **retrait** (`Compta` absent — l'énumération tombe à 4
tandis que « **5 onglets** » reste juste, aveugle au compte) ou un **réordre**
laisse le compte à 5 pendant que l'artefact dit autre chose. « Vert trompeur »
qu'aucune suite `tests/` (qui teste des FONCTIONS, pas la prose) n'attrape.
- **État courant** : **aucun onglet périmé** — la liste des 5 recoupe l'artefact
exactement (anti-invention #6, rien à réécrire). Le défaut est la **surface ungated**.
**Nouveau bloc dans `ci/check_readme_claims.sh`** : recompute la **liste ordonnée**
des noms courts depuis `role_navigation.json` (zéro duplication de la navigation du
générateur · #6) et exige que la parenthèse l'énumère **exactement** : contrôle par
**ENSEMBLE** (absent ET en trop mordus via set-diff), puis **ordre**. Même patron que
l'énumération des confirmations DevOps, la carte de renormalisation par archétype 4Big
ou la table « Verdict courant » de l'audit 5D. Cohérence croisée en bonus :
`len(role_navigation) == MANIFEST.counts.onglets == MANIFEST.counts.portails` (la
bijection du générateur) — mord un compteur du manifeste désynchronisé de sa navigation.
### Preuve (4 morsures vérifiées + restauration verte)
| Injection | Résultat |
|---|---|
| échange `Ventes → Marketing` (compte reste 5 · gate-compte aveugle) | ✗ énumère `[Marketing,…]` MAIS `role_navigation` `[Ventes,…]` · absents=`[Ventes]` · en trop=`[Marketing]` · exit 1 |
| retrait `Compta` (énumération=4 · « 5 onglets » reste juste) | ✗ énumère 4 MAIS `role_navigation` 5 · absents=`[Compta]` · exit 1 |
| réordre `Construction/Ventes/…` (même ensemble) | ✗ même ensemble, ordre ≠ · exit 1 |
| énumération supprimée (claim disparu) | ✗ énumération des onglets INTROUVABLE · exit 1 |
| restauré | ✓ `Ventes/Construction/Achat/Compta/Direction` == `role_navigation` · bijection 5==onglets==portails · exit 0 |
- **`ci/README.md`** — surface ajoutée à la table récap du pipeline + paragraphe
descriptif (2ᵉ surface du même README mobile).
Suite CI complète re-verte (7 gates) : `guard_constraints` · `validate_json` ·
`check_docs` · `check_artifacts` · `check_regression` · `check_ci_integrity` ·
`check_readme_claims`.
**Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ;
édition **hors** `05_deliverables_mvp` ⇒ 0 dérive d'artefact).
**Auto-score 4Big** : 96/100.
---
## Session `20260731_143104` · Buffer S8 · Domaine QA/Audit 5D (2e surface) : la **table « Verdict courant »** de l'audit de conformité énumérait À LA MAIN ses **4 open items** (`control → dimension → propriétaire`) sans gate d'IDENTITÉ — le bloc existant ne recompute que le COMPTE (« 4 à confirmer »), aveugle à un **échange** d'open item. ## Session `20260731_143104` · Buffer S8 · Domaine QA/Audit 5D (2e surface) : la **table « Verdict courant »** de l'audit de conformité énumérait À LA MAIN ses **4 open items** (`control → dimension → propriétaire`) sans gate d'IDENTITÉ — le bloc existant ne recompute que le COMPTE (« 4 à confirmer »), aveugle à un **échange** d'open item.
**Tâche** : **Sprint 8 · buffer L75** (DevOps CI/CD · QA). Roadmap fonctionnellement **Tâche** : **Sprint 8 · buffer L75** (DevOps CI/CD · QA). Roadmap fonctionnellement
+13 -2
View File
@@ -20,7 +20,7 @@ et manuellement (`workflow_dispatch`). Jobs statiques (+ une suite `unittest` pa
| `check-artifacts` | `ci/check_artifacts.sh` | Reproductibilité : chaque `out/*.json` versionné == build frais | ✅ oui | | `check-artifacts` | `ci/check_artifacts.sh` | Reproductibilité : chaque `out/*.json` versionné == build frais | ✅ oui |
| `check-regression` | `ci/check_regression.sh` | Fraîcheur : `qa/regression/out/regression_run.json` (`run`) == run frais + verdict PASS | ✅ oui | | `check-regression` | `ci/check_regression.sh` | Fraîcheur : `qa/regression/out/regression_run.json` (`run`) == run frais + verdict PASS | ✅ oui |
| `check-ci-integrity` | `ci/check_ci_integrity.sh` | Intégrité du câblage : `gate.needs` == tous les jobs non-manuels · chaque `ci/*.sh` câblé au gate | ✅ oui | | `check-ci-integrity` | `ci/check_ci_integrity.sh` | Intégrité du câblage : `gate.needs` == tous les jobs non-manuels · chaque `ci/*.sh` câblé au gate | ✅ oui |
| `check-readme-claims` | `ci/check_readme_claims.sh` | Intégrité des chiffres des docs d'entrée : chaque nombre de `README.md` « État courant », de la fiche QA « Verdict agrégé » **et** de la colonne « Tests » par suite des tables de fiches **et** des agrégats en prose (Total CRM / RBAC / e-CF) **et** des comptes par workspace des portails (cartes/liens/rôles, ×2 docs) **et** des comptes RBAC des 3 volets (table Nb rôles/profil, ligne agrégée apply_plan, ventilation userperm) **et** des comptes de mots-clés SEO (total + fr/en/es, ×2 docs) **et** du pipeline vente CRM (états/transitions, ×2 docs) **et** de l'app mobile Expo (onglets/rôles/langues/identifiants a_confirmer) **et** du DocType CONFOTUR (champs/données/sections/rôles/évènements de dépôt, ×2 docs) **et** de l'audit 5D (contrôles/dimensions ×2 docs + ventilation verdict pass/fail/à-confirmer + énumération des open items `control→dimension→propriétaire` recomputée de `audit_report.open_items`) **et** du run-book de déploiement VPS (phases ×2 dans la fiche devops + confirmations préalables count & énumération dans le README) **et** du run-sheet de démo (nombre de modules cités dans le diagramme du README) **et** du montage Chat OTOIA par portail (`Custom Block`/configs runtime/portails, tous = 5 aujourd'hui, ×2 docs) **et** du module de recette `qa/acceptance` (couple livrables de sprint + métriques succès MVP, ×2 docs de module au-delà du README d'entrée) **et** du barème de l'auditeur 4Big (nombre de critères en prose + poids par critère de la table + « generator (N critères) », recomputés de `quality_report.criteria`) **et** de la carte de renormalisation par archétype de l'auditeur 4Big (critères EXCLUS de `data_room`/`parser`/`contract`, recomputés des `checks` par archétype de `quality_report.modules`) == artefact cité (#6) | ✅ oui | | `check-readme-claims` | `ci/check_readme_claims.sh` | Intégrité des chiffres des docs d'entrée : chaque nombre de `README.md` « État courant », de la fiche QA « Verdict agrégé » **et** de la colonne « Tests » par suite des tables de fiches **et** des agrégats en prose (Total CRM / RBAC / e-CF) **et** des comptes par workspace des portails (cartes/liens/rôles, ×2 docs) **et** des comptes RBAC des 3 volets (table Nb rôles/profil, ligne agrégée apply_plan, ventilation userperm) **et** des comptes de mots-clés SEO (total + fr/en/es, ×2 docs) **et** du pipeline vente CRM (états/transitions, ×2 docs) **et** de l'app mobile Expo (onglets/rôles/langues/identifiants a_confirmer + énumération ordonnée des onglets recomputée de `role_navigation`, bijection onglet⇔portail) **et** du DocType CONFOTUR (champs/données/sections/rôles/évènements de dépôt, ×2 docs) **et** de l'audit 5D (contrôles/dimensions ×2 docs + ventilation verdict pass/fail/à-confirmer + énumération des open items `control→dimension→propriétaire` recomputée de `audit_report.open_items`) **et** du run-book de déploiement VPS (phases ×2 dans la fiche devops + confirmations préalables count & énumération dans le README) **et** du run-sheet de démo (nombre de modules cités dans le diagramme du README) **et** du montage Chat OTOIA par portail (`Custom Block`/configs runtime/portails, tous = 5 aujourd'hui, ×2 docs) **et** du module de recette `qa/acceptance` (couple livrables de sprint + métriques succès MVP, ×2 docs de module au-delà du README d'entrée) **et** du barème de l'auditeur 4Big (nombre de critères en prose + poids par critère de la table + « generator (N critères) », recomputés de `quality_report.criteria`) **et** de la carte de renormalisation par archétype de l'auditeur 4Big (critères EXCLUS de `data_room`/`parser`/`contract`, recomputés des `checks` par archétype de `quality_report.modules`) == artefact cité (#6) | ✅ oui |
| `gate` | — | Agrégat vert = gate qualité 4Big franchi | ✅ oui | | `gate` | — | Agrégat vert = gate qualité 4Big franchi | ✅ oui |
Aucune dépendance réseau/marketplace hors `actions/checkout`. Tout tourne avec Aucune dépendance réseau/marketplace hors `actions/checkout`. Tout tourne avec
@@ -207,7 +207,18 @@ EAS/stores jamais fabriqués). Les quatre sont recomputés depuis
`mobile/app_config/out/MANIFEST.json` (`counts.{onglets,roles_couverts,langues, `mobile/app_config/out/MANIFEST.json` (`counts.{onglets,roles_couverts,langues,
identifiants_a_confirmer}`, byte-gaté par `check_artifacts`). Recomputer **les identifiants_a_confirmer}`, byte-gaté par `check_artifacts`). Recomputer **les
quatre** volets mord une dérive de n'importe lequel (ajouter un rôle/portail RBAC, quatre** volets mord une dérive de n'importe lequel (ajouter un rôle/portail RBAC,
une langue, un credential store) là où un seul nombre resterait aveugle. une langue, un credential store) là où un seul nombre resterait aveugle. **2ᵉ
surface du même README** : la même ligne récap **énumère** les 5 onglets
(« (Ventes/Construction/Achat/Compta/Direction) ») — le patron du compte matche
la parenthèse par `\([^)]*\)` mais en **ignore le contenu**. Cette liste est
data-derived : `mobile/app_config/out/role_navigation.json` (byte-gaté) porte un
onglet par portail métier (bijection 1 onglet ⇔ 1 portail), chaque entrée = son
`label` « OTO <Nom> ». On recompute la **liste ordonnée** des noms courts et on
exige que la parenthèse l'énumère **exactement** : un **échange** (Ventes→
Marketing), un **retrait** (Compta absent — l'énumération tombe à 4 tandis que
« **5 onglets** » reste juste) ou un **réordre** est mordu là où le seul compte
est aveugle. Cohérence croisée : `len(role_navigation) == counts.onglets ==
counts.portails` — mord un compteur du manifeste désynchronisé de sa navigation.
Même classe enfin sur le **DocType CONFOTUR Application** (livrable `legal/confotur`) : Même classe enfin sur le **DocType CONFOTUR Application** (livrable `legal/confotur`) :
la ligne de synthèse du README du module (« **18 champs** (14 de donnée) · **4 la ligne de synthèse du README du module (« **18 champs** (14 de donnée) · **4
+62
View File
@@ -886,6 +886,68 @@ if mob_exp is not None:
bad(f"Mobile · {MOB_RD} — dit {'/'.join(m.groups())} MAIS MANIFEST dit " bad(f"Mobile · {MOB_RD} — dit {'/'.join(m.groups())} MAIS MANIFEST dit "
f"{'/'.join(mob_exp)} ({mob_src}) → régénérer le README") f"{'/'.join(mob_exp)} ({mob_src}) → régénérer le README")
# ============================================================================
# Mobile (2e surface) · ÉNUMÉRATION des onglets de l'app Expo — même README, même
# ligne récap « **5 onglets** (Ventes/Construction/Achat/Compta/Direction) ».
# ----------------------------------------------------------------------------
# Le bloc mobile ci-dessus recompute le COMPTE d'onglets (« 5 ») mais son patron
# ne mord QUE le nombre : la parenthèse qui ÉNUMÈRE les 5 portails est matchée par
# `\([^)]*\)` et son CONTENU est IGNORÉ. Or cette liste est DATA-DERIVED : le
# générateur app_config émet out/role_navigation.json (byte-gaté par
# check_artifacts ⇒ artefact faisant autorité) = un onglet par portail métier du
# contrat RBAC (bijection 1 onglet ⇔ 1 portail · `plateforme` exclu), chaque
# entrée portant `portail` (id) + `label` (« OTO <Nom> »). Le nom court énuméré au
# README = le label sans le préfixe « OTO ». Un ÉCHANGE (Ventes→Marketing), un
# RETRAIT (Compta absent — l'énumération tombe à 4 tandis que « **5 onglets** »
# reste juste) ou un RÉORDRE ferait dériver la prose EN SILENCE pendant que
# l'artefact dit autre chose : « vert trompeur » aveugle au seul compte, qu'aucune
# suite tests/ (qui teste des FONCTIONS, pas la prose) n'attrape — même patron que
# l'énumération des confirmations DevOps, la carte de renormalisation par archétype
# 4Big ou la table « Verdict courant » de l'audit 5D. On RECOMPUTE ici la LISTE
# ORDONNÉE des noms courts depuis role_navigation.json (zéro duplication de la
# navigation du générateur) et on exige que la parenthèse l'énumère EXACTEMENT
# (absent ET en trop mordus, puis ordre). Cohérence croisée en bonus :
# len(role_navigation) == MANIFEST.counts.onglets == MANIFEST.counts.portails (la
# bijection du générateur) — mord un compteur du manifeste désynchronisé de sa
# propre liste de navigation.
# ============================================================================
try:
mob_nav = load("mobile/app_config/out/role_navigation.json")
# nom court = label « OTO <Nom> » sans le préfixe « OTO » (ce que le doc copie)
mob_names = [re.sub(r"^OTO\s+", "", e["label"]).strip() for e in mob_nav]
mob_names_c = load("mobile/app_config/out/MANIFEST.json")["counts"]
except (OSError, KeyError, ValueError, TypeError) as e:
bad(f"Mobile · role_navigation/MANIFEST illisible/inattendu : {e}"); mob_names = None
if mob_names is not None:
# bijection : la liste de navigation == compteurs onglets ET portails du manifeste
n_nav = len(mob_nav)
if not (n_nav == mob_names_c.get("onglets") == mob_names_c.get("portails")):
bad(f"Mobile · bijection rompue : |role_navigation|={n_nav} MAIS "
f"MANIFEST onglets={mob_names_c.get('onglets')} "
f"portails={mob_names_c.get('portails')} → régénérer l'artefact")
else:
good(f"Mobile · bijection onglet⇔portail : {n_nav} == onglets == portails")
# énumération de la parenthèse : capturer le contenu entre ( ) qui suit « N onglets »
mob_enum_pat = r"\*\*\d+ onglets\*\* \(([^)]*)\)"
mob_txt2 = read_norm(MOB_RD)
if mob_txt2 is not None:
me = re.search(mob_enum_pat, mob_txt2)
if me is None:
bad(f"Mobile · {MOB_RD} — énumération des onglets INTROUVABLE "
f"(attendu {'/'.join(mob_names)})")
else:
got = [s.strip() for s in me.group(1).split("/") if s.strip()]
if got == mob_names:
good(f"Mobile · {MOB_RD} — énumération onglets {'/'.join(got)} "
f"== role_navigation")
else:
miss = [x for x in mob_names if x not in got]
extra = [x for x in got if x not in mob_names]
detail = (f" · absents={miss}" if miss else "") + \
(f" · en trop={extra}" if extra else "") or " · même ensemble, ordre ≠"
bad(f"Mobile · {MOB_RD} — énumère {got} MAIS role_navigation dit "
f"{mob_names}{detail} → régénérer le README")
# ============================================================================ # ============================================================================
# Legal · DocType CONFOTUR Application (champs / sections / rôles / évènements de # Legal · DocType CONFOTUR Application (champs / sections / rôles / évènements de
# dépôt) — même classe de « vert trompeur », transcrit À LA MAIN dans DEUX docs. # dépôt) — même classe de « vert trompeur », transcrit À LA MAIN dans DEUX docs.