Compare commits

..

4 Commits

Author SHA1 Message Date
Claude Code DTP Worker d1e96d7784 [DTP-Worker 20260801_180411] Auto exec · session 20260801_180411
CI / Contraintes NON-NÉGOCIABLES (CLAUDE.md) (push) Has been cancelled
CI / Validation JSON (schémas Faisabilité) (push) Has been cancelled
CI / Qualité documentaire (liens + 4Big) (push) Has been cancelled
CI / Reproductibilité des artefacts out/ (build == commité) (push) Has been cancelled
CI / Fraîcheur matrice de régression (run == commité) (push) Has been cancelled
CI / Intégrité du câblage CI (gate agrège tout · gates statiques verrouillés) (push) Has been cancelled
CI / Intégrité des chiffres du README (valeur == artefact cité · (push) Has been cancelled
CI / Publiciste · parser + schéma + generator (unittest) (push) Has been cancelled
CI / RBAC · 50 rôles + schéma (unittest) (push) Has been cancelled
CI / Faisabilité · générateur 4 volets + round-trip (unittest) (push) Has been cancelled
CI / RBAC · fixtures ERPNext (Role + Custom DocPerm) (push) Has been cancelled
CI / RBAC · plan User Permission (row-level) (push) Has been cancelled
CI / RBAC · Role Profile (bundles par portail) (push) Has been cancelled
CI / RBAC · run-book d'application unifié (agrégat 3 volets) (push) Has been cancelled
CI / Faisabilité · dossier bancable trilingue FR/EN/ES (push) Has been cancelled
CI / CRM · workflow vente ERPNext (lead → CONFOTUR) (push) Has been cancelled
CI / CRM · DocType porteur OTO Dossier Vente (push) Has been cancelled
CI / CRM · barème commissions vendeurs (push) Has been cancelled
CI / Fiscal · e-CF DGII (Compupar) (push) Has been cancelled
CI / Frontend · Workspaces 5 portails rôle (push) Has been cancelled
CI / Legal · DocType CONFOTUR Application (push) Has been cancelled
CI / QA · Audit 5D conformité (push) Has been cancelled
CI / SEO · mots-clés trilingues + schema.org + hreflang (push) Has been cancelled
CI / Chat OTOIA · montage par portail (Custom Block) (push) Has been cancelled
CI / QA · Audit 4Big (95+/100 sur 100% deliverables) (push) Has been cancelled
CI / Démo · Scénarios (run-sheet P07 banquier / P05 client) (push) Has been cancelled
CI / QA · Matrice de régression exhaustive (Sprint 8) (push) Has been cancelled
CI / DevOps · Run-book de déploiement VPS unifié (Sprint 8) (push) Has been cancelled
CI / QA · Matrice d'acceptation / traçabilité MVP (Sprint 8) (push) Has been cancelled
CI / Mobile · config app Expo/EAS (navigation par rôle) (push) Has been cancelled
CI / E2E baseline Playwright (manuel) (push) Has been cancelled
CI / Gate qualité (agrégat) (push) Has been cancelled
2026-08-01 18:19:11 +00:00
Claude Code DTP Worker e874717f0f [DTP-Worker] Sprint 8 · buffer · Fiches agents/voix : gate d'ANCRAGE du pied de page « Voix Amélie QC (multilingual_v2) pour toute interaction OTOIA » — répété dans 11 des 13 fiches 03_agents/*/AGENT.md (hors faisabilite/publiciste) — sur CLAUDE.md §Architecture cible
Le triplet persona/qualif/voix (Amélie · QC · multilingual_v2) recopié à la main
dans chaque pied de page était HORS de tout gate : le bloc persona Chat OTOIA
(063234) ne confronte que le module chat_otoia, aveugle aux 11 fiches ; le bloc
configs runtime note même que le « QC » est « gardé en littéral — non gaté ici ».

Bloc ajouté (check_readme_claims.sh) : RE-DÉRIVATION INDÉPENDANTE du triplet de
§Architecture cible (regex qui ne fige PAS le qualificatif en dur ⇒ QC→… mord ici,
pas seulement via l'INTROUVABLE du bloc persona) · 3 tokens == CLAUDE.md exigés sur
chaque fiche portant le pied de page · pied de page malformé & évaporation totale du
recensement échouent AUSSI (#6). 6 morsures vérifiées (CLAUDE.md voix v2→v3 : 11
fiches + cascade persona · CLAUDE.md qualif QC→BC : 11 fiches, bloc indépendant ·
seo voix · crm persona · bim malformé · surface évaporée) · restauré green · 7 gates
re-verts. ci/README.md (récap + paragraphe détaillé) mis à jour.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 17:44:15 +00:00
Claude Code DTP Worker b9e333ceef [DTP-Worker] Sprint 8 · buffer · QA/audit_4big : gate des SEUILS STRUCTURELS de la colonne « Fait vérifié »
3e surface du MÊME README audit_4big, jamais gatée : les deux seuils que le
scorer applique (DOC « ≥ 400 octets » · TESTS « ≥ 8 méthodes def test_* »),
transcrits à la main. Le bloc BARÈME ne capture que le POIDS voisin (regex
`| `DOC` | 20 |`), aveugle à la 3e cellule. Ces nombres sont DATA-DERIVED de
quality_spec.json[thresholds] (min_doc_bytes/min_test_methods) — l'INPUT
byte-gaté par check_artifacts (report rebuild depuis lui).

Gate ajouté (check_readme_claims.sh) : (1) cross-check d'ANCRAGE — le seuil
TESTS APPLIQUÉ, lu de l'evidence « (seuil N) » de CHAQUE module du report ==
spec.min_test_methods pour tous (mord une divergence spec↔report) ; (2) prose
DOC « ≥ N octets » == spec ; (3) prose TESTS « ≥ N méthodes » == spec ; claim
absent échoue AUSSI (traçabilité #6). Élever min_test_methods (8→12 : module de
8-11 tests sous FAIL) ou changer min_doc_bytes rebuild le report SANS toucher au
README → prose périmée en silence, désormais interdite.

5 morsures vérifiées (README 400→450 · README 8→10 · ligne DOC vidée · spec
min_test_methods 8→12 rebuild : README+cross-check · spec min_doc_bytes 400→500
rebuild) ; restauré = green · 7 gates re-verts. ci/README.md (récap + paragraphe
détaillé) mis à jour. Hors périmètre VPS : néant.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 17:12:01 +00:00
Claude Code DTP Worker a52daa056f [DTP-Worker] Sprint 8 · buffer · QA/audit_5d : gate d'IDENTITÉ des 5 DIMENSIONS du cadre
3e surface du MÊME README `qa/audit_5d` : la table « ## Les 5 dimensions »
(README:15-19) transcrit par ligne le triplet (id · titre · reference) de chaque
dimension. Jusqu'ici gatée seulement sur son COMPTE agrégé (« 5 dimensions ») — un
WILDCARD aveugle à QUELLE dimension. Une norme de référence permutée (D3 fiscal
`Ley 32-23`, D1 probatoire `ISA 500`) est une affirmation de conformité fausse
(l'invention même que #6 interdit) sur l'audit de second niveau que le banquier lit.

Gate ajouté (`ci/check_readme_claims.sh`) : liste ORDONNÉE {id:(titre,reference)}
recomputée de `qa/audit_5d/out/audit_report.json[dimensions]` (byte-gaté) — identité
par ensemble d'id (absent/en trop), ordre préservé (D1→D5), (titre,reference) par
ligne. Cohérence croisée bonus : rapport == audit_spec.json[dimensions] (l'INPUT dont
il dérive). Table disparue = échec (traçabilité #6).

8 morsures vérifiées (README rename titre/permute norme ISA/Ley/supprime ligne/
réordonne · artefact & spec renommés · table supprimée) ; restauré green · 7 gates
re-verts. Édition hors `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact.
ci/README.md (recap + paragraphe détaillé) mis à jour.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 16:41:34 +00:00
5 changed files with 475 additions and 4 deletions
+2 -2
View File
@@ -21,8 +21,8 @@ AVANT le Workflow** qui le cible.
| Module | Sprint | Rôle | Entrée CLI | Job CI | Tests |
|---|---|---|---|---|---|
| [`workflow_vente/`](../../05_deliverables_mvp/crm/workflow_vente/README.md) | 4 (roadmap L51) | Graphe **Workflow** ERPNext : le pipeline `lead → visite → devis → réservation → contrat → CONFOTUR` (states + transitions + actions) | `workflow_vente_gen.py build\|validate` | `crm-workflow-vente-tests` | 25 |
| [`dossier_vente/`](../../05_deliverables_mvp/crm/dossier_vente/README.md) | 4 (roadmap L51) | **DocType porteur** `OTO Dossier Vente` : le document réel qui circule dans le Workflow ; sans lui le pipeline n'a rien à quoi s'attacher | `doctype_dossier_vente_gen.py build\|validate` | `crm-dossier-vente-tests` | 31 |
| [`workflow_vente/`](../../05_deliverables_mvp/crm/workflow_vente/README.md) | 4 (roadmap L50) | Graphe **Workflow** ERPNext : le pipeline `lead → visite → devis → réservation → contrat → CONFOTUR` (states + transitions + actions) | `workflow_vente_gen.py build\|validate` | `crm-workflow-vente-tests` | 25 |
| [`dossier_vente/`](../../05_deliverables_mvp/crm/dossier_vente/README.md) | 4 (roadmap L50) | **DocType porteur** `OTO Dossier Vente` : le document réel qui circule dans le Workflow ; sans lui le pipeline n'a rien à quoi s'attacher | `doctype_dossier_vente_gen.py build\|validate` | `crm-dossier-vente-tests` | 31 |
| [`commissions/`](../../05_deliverables_mvp/crm/commissions/README.md) | 4 (roadmap L51) | Barème **commissions vendeurs** : quel évènement du pipeline paie, à quel rôle, sur quel montant + calculateur traçable `commission = base × taux` | `commissions_gen.py build\|validate` | `crm-commissions-tests` | 25 |
**Trois modules cross-cohérents, une source unique** (CLAUDE.md #5 · éliminer les
+1 -1
View File
@@ -34,7 +34,7 @@ contractualisée, pas inventée :
| Contact in-repo (vérifié) | Ce qu'il fixe |
|---|---|
| `05_deliverables_mvp/rbac/rbac_50_roles.json` → rôle `plateforme-mobile` | rôle ERPNext **`OTO Plateforme Mobile`** (« Développeur Mobile » · famille/portail **`plateforme`** · entité **9060 QC** · niveau 2 · scope `groupe` · modules `Core`+`Website` · perm `API Access` custom R/W · description « Maintient l'app **Expo/React Native** et l'**API mobile** ; gère les **builds EAS** et les **soumissions stores** ») |
| `05_deliverables_mvp/qa/acceptance/acceptance_spec.json` → row **S5** (roadmap L58) | l'acceptation Sprint 5 « Apps mobile live 2 stores » ; les « **builds Expo 54 et submit App Store / Play Store** » y sont explicitement listés `out_of_scope` (source : roadmap Sprint 5 · Mobile L57 · CLAUDE.md #8) — l'honnêteté du périmètre est déjà gatée QA |
| `05_deliverables_mvp/qa/acceptance/acceptance_spec.json` → row **S5** (roadmap L58) | l'acceptation Sprint 5 « Apps mobile live 2 stores » ; les « **builds Expo 54 et submit App Store / Play Store** » y sont explicitement listés `out_of_scope` (source : roadmap Sprint 5 · Mobile L56 · CLAUDE.md #8) — l'honnêteté du périmètre est déjà gatée QA |
> Note d'honnêteté (anti-invention #6) : le rôle mobile est de famille
> **`plateforme`** (9060 QC), et non `construction`/`vente`. Il n'est donc
+173
View File
@@ -1,5 +1,116 @@
# Activity Log · 2026-08-01 · Claude Code DTP
## Session `20260801_173409` · Buffer S8 · Fiches agents/pied de page voix OTOIA : le **PIED DE PAGE CANONIQUE** « - Voix **Amélie QC** (**multilingual_v2**) pour toute interaction OTOIA » — répété à l'identique dans **11 des 13 fiches** `03_agents/*/AGENT.md` (toutes sauf `faisabilite`/`publiciste`, qui n'orchestrent pas OTOIA) — RECOPIE À LA MAIN les **trois** tokens de la contrainte **CLAUDE.md §Architecture cible** « Voix Amélie QC (multilingual_v2) » : prénom persona (`Amélie`), qualificatif de marque (`QC`), modèle de voix ElevenLabs (`multilingual_v2`). Cette surface était **HORS de tout gate**.
**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 la persona/capabilities
`frontend/chat_otoia` ancrées sur §Architecture cible, la marque SEO org (§Entités) et
les tokens branding (#4) — appliqué à une **surface distincte encore ungated** : les 11
pieds de page de fiches, jamais confrontés à CLAUDE.md.
**Dérive silencieuse fermée** :
- `03_agents/{bim,crm,devops,erpnext_backend,frontend_console,ifc_speckle,mobile,
onapi_legal,qa,rendu,seo}/AGENT.md` — ligne « - Voix Amélie QC (multilingual_v2)
pour toute interaction OTOIA ».
- Source faisant autorité : **CLAUDE.md §Architecture cible** (ligne 30) « Voix
Amélie QC (multilingual_v2) ».
- Piège : le bloc « Chat OTOIA · IDENTITÉ de la persona » RE-DÉRIVE bien persona+voix
de CLAUDE.md mais ne confronte QUE le module `chat_otoia` (spec + `out/` + son propre
README + l'oracle du test) — **AVEUGLE aux 11 pieds de page** ; le bloc « configs
runtime » note même explicitement que le « QC » est « gardé en littéral — non gaté
ici ». Le jour où ElevenLabs déprécie `multilingual_v2` (→`multilingual_v3`) ou qu'on
renomme la persona/le qualificatif dans CLAUDE.md, ces 11 fiches annonceraient EN
SILENCE une voix/persona que le mandat ne prescrit plus, chacune affirmant orchestrer
« toute interaction OTOIA » avec le mauvais modèle. Aucune suite `tests/` (FONCTIONS
du builder, jamais la prose des fiches) n'attrape ce « vert trompeur ».
- **État courant** : **aucune valeur périmée** — les 11 pieds de page recoupent
CLAUDE.md §Architecture cible exactement (anti-invention #6, rien à réécrire). Le
défaut est la **surface ungated**.
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « Fiches agents · pied de
page voix » après le bloc persona/capabilities Chat OTOIA) : on RE-DÉRIVE le triplet
(persona · qualif · voix) de CLAUDE.md §Architecture cible **INDÉPENDAMMENT** du parse
persona amont (dont la regex fige `QC` en dur — un renommage du qualificatif doit mordre
ICI, pas seulement via l'INTROUVABLE du bloc persona) ; pour CHAQUE fiche git-trackée
portant le pied de page, on exige ses **3 tokens == CLAUDE.md**. Une fiche avec prose
« interaction OTOIA » mais pied de page malformé échoue ; la **disparition totale** du
recensement échoue AUSSI (surface évaporée = régression #6). Direction fiche→CLAUDE.md.
**6 morsures vérifiées** : CLAUDE.md `multilingual_v2→v3` (mord les 11 fiches +, en
cascade, le bloc persona amont — 15 fails, l'ancre est vive) · CLAUDE.md qualif `QC→BC`
(mord les 11 pieds de page — bloc INDÉPENDANT, ne s'appuie pas sur le parse persona qui
fige QC) · fiche `seo` voix `v2→v1` (1 fail) · fiche `crm` persona `Amélie→Sophie`
(1 fail) · fiche `bim` pied de page malformé, parenthèses retirées (1 fail INTROUVABLE) ·
pied de page retiré de TOUTES les fiches (surface évaporée, 1 fail) ; restauré = green :
11 pieds de page == CLAUDE.md · exit 0. Working tree byte-restauré (`git checkout --`,
**JAMAIS** `git clean`, interdit absolu) · **7 gates re-verts**
(`check_readme_claims`/`check_ci_integrity`/`check_docs`/`guard_constraints`/
`check_artifacts`/`check_regression`/`validate_json`).
- `ci/README.md` (clause de la table récap du pipeline + paragraphe détaillé « Pied de
page Voix Amélie QC (multilingual_v2) des fiches agents ancré sur CLAUDE.md
§Architecture cible ») 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 ; le runtime réel
de la voix OTOIA reste côté VPS).
- **Auto-score 4Big** : 96/100.
## Session `20260801_170404` · Buffer S8 · Domaine QA/audit_4big : la colonne **« Fait vérifié »** de la table du barème (`qa/audit_4big/README.md:24,26`) — les **DEUX SEUILS STRUCTURELS** que le scorer applique réellement (`DOC` « ≥ **400 octets** » · `TESTS` « ≥ **8 méthodes** `def test_*` »), transcrits À LA MAIN — étaient **HORS de tout gate**. Le bloc BARÈME existant recompute la 2e colonne (**poids** : DOC 20 · CONTRAT 20 · TESTS 25 · CLI 15 · HANDOFF 20) mais son regex (`| \`DOC\` | 20 |`) est **AVEUGLE à la 3e cellule** ; ces deux nombres sont DATA-DERIVED de `quality_spec.json[thresholds]` (`min_doc_bytes`/`min_test_methods`).
**Tâche** : **Sprint 8 · buffer** (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive (CLAUDE.md #6). Même patron EXACT que le
bloc BARÈME (poids) et la carte de renormalisation par archétype du MÊME README —
appliqué à une **3e surface distincte du MÊME README `qa/audit_4big`** : les seuils
de la colonne « Fait vérifié », jamais gatés hors leur poids voisin.
**Dérive silencieuse fermée** :
- `05_deliverables_mvp/qa/audit_4big/README.md:24` — cellule DOC « un `*.md`
(README/SPEC) ≥ **400 octets** à la racine » (= `thresholds.min_doc_bytes`) ·
`:26` — cellule TESTS « ≥ **8 méthodes** `def test_*` dans `tests/` » (=
`thresholds.min_test_methods`).
- Source faisant autorité : `05_deliverables_mvp/qa/audit_4big/quality_spec.json`
`[thresholds]` — l'**INPUT** du générateur, byte-gaté indirectement par
`check_artifacts` qui prouve que `out/quality_report.json` se **rebuild** depuis
lui. Les seuils ne sont PAS émis en clair dans le report ; seule l'**evidence
`TESTS`** de chaque module porte « (seuil N) » (émise par `crit_tests`), ce qui en
fait un **ancrage croisé** du seuil réellement APPLIQUÉ.
- Piège : la seule vérification existante (`tests/`) teste des **FONCTIONS** de
scoring (`crit_doc`/`crit_tests` avec seuil paramétré), jamais la PROSE du README.
Élever `min_test_methods` (8→12 : un module de 8-11 tests tomberait sous `FAIL` —
la barre même que le critère pose) ou changer `min_doc_bytes` dans le spec rebuild
le report SANS TOUCHER au README → prose « ≥ 8 méthodes »/« ≥ 400 octets » périmée
en silence, l'auditeur documentant un barème qu'il **n'applique plus** (l'invention
#6 interdit).
- **État courant** : **aucun seuil périmé** — les 2 cellules recoupent le spec
exactement, et le seuil TESTS appliqué (evidence report) == spec sur les 22 modules
(anti-invention #6, rien à réécrire). Le défaut est la **surface ungated**.
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « README audit_4big ·
SEUILS STRUCTURELS » après le bloc renormalisation par archétype) : (1) **cross-check
d'ancrage** — le seuil `TESTS` APPLIQUÉ (regex `(seuil N)` sur l'evidence de CHAQUE
module du report) == `spec.min_test_methods` pour tous (mord une divergence
spec↔report) ; (2) **prose DOC** — cellule « ≥ N octets » recomputée du spec, exacte ;
(3) **prose TESTS** — cellule « ≥ N méthodes » recomputée du spec, exacte. Un claim
absent échoue AUSSI (traçabilité #6).
**5 morsures vérifiées** : README `DOC 400→450` (prose périmée) · README `TESTS 8→10`
(prose périmée) · README ligne DOC vidée du seuil (claim INTROUVABLE) · **spec**
`min_test_methods 8→12` + rebuild (README périmé ET cross-check evidence[8]≠spec[12]
spec↔report) · **spec** `min_doc_bytes 400→500` + rebuild (README DOC périmé) ;
restauré = green : seuil TESTS appliqué == spec (8) · DOC « ≥ 400 octets » == spec ·
TESTS « ≥ 8 méthodes » == spec · exit 0. Working tree byte-restauré (`git checkout --`,
**JAMAIS** `git clean`, interdit absolu) · **7 gates re-verts**
(`check_readme_claims`/`check_ci_integrity`/`check_docs`/`guard_constraints`/
`check_artifacts`/`check_regression`/`validate_json`).
- `ci/README.md` (ligne récap du pipeline `check-readme-claims` + paragraphe détaillé
« troisième surface · colonne Fait vérifié / seuils structurels ») 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 ; le branchement
du gate 4Big sur le pipeline de release reste côté VPS).
- **Auto-score 4Big** : 96/100.
## Session `20260801_000201` · Buffer S8 · Domaine RBAC/userperm_gen : la **table « Mapping `scope_donnees` → mécanisme »** — la **FONCTION d'enforcement ROW-LEVEL** (quel mécanisme Frappe natif applique CHAQUE portée de données + si un `User Permission` template est émis), CŒUR sécurité du module — était transcrite EN PROSE (README:33-38) SANS AUCUN gate d'IDENTITÉ. Le bloc RBAC « 3 volets » existant ne gate QUE la **ventilation** par mécanisme (« 28 `entite` · 16 `groupe` · 2 `own` · 4 `equipe` »), un COMPTE aveugle à QUEL mécanisme applique QUELLE portée.
**Tâche** : **Sprint 8 · buffer** (DevOps CI/CD · QA). Roadmap fonctionnellement
@@ -1838,3 +1949,65 @@ toucher portails (prouve la désambiguïsation par job CI). Restauré = green :
édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact ; les suites
réelles tournent côté runner Gitea).
- **Auto-score 4Big** : 96/100.
## Session `20260801_163401` · Buffer S8 · Domaine QA/audit_5d : l'**IDENTITÉ des 5 DIMENSIONS** du cadre d'audit — le **libellé** (`titre`) ET la **norme de référence** (`reference`) de chacune, énumérés à la main dans la table « ## Les 5 dimensions » (`05_deliverables_mvp/qa/audit_5d/README.md:15-19`) — était **HORS de tout gate d'IDENTITÉ**. Le tout premier bloc audit_5d ne recompute que le **COMPTE** (« **5 dimensions (5D)** ») — un WILDCARD aveugle à QUELLE dimension ; les 2 autres blocs audit_5d gatent la ventilation du verdict et l'énumération des open items, jamais la carte des dimensions.
**Tâche** : **Sprint 8 · buffer** (DevOps CI/CD · QA — « Audit UAF + normes ISA/IFRS
5D »). Roadmap fonctionnellement close ; poursuite de la série anti-dérive (CLAUDE.md
#6). **Même patron EXACT** que l'énumération des open items (2e surface du MÊME
README) ou les triplets par workspace des portails — appliqué à une **3e surface
distincte du MÊME README `qa/audit_5d`** : la table des 5 dimensions, gatée jusqu'ici
seulement sur son COMPTE agrégé.
**Dérive silencieuse fermée** :
- `05_deliverables_mvp/qa/audit_5d/README.md:15-19` — table « ## Les 5 dimensions » :
chaque ligne `| **D1** | Traçabilité & anti-invention | ISA 500 — Éléments probants |`
transcrit PAR LIGNE le triplet (`id`, `titre`, `reference`) d'une dimension.
- Source faisant autorité : `qa/audit_5d/out/audit_report.json[dimensions]`
(byte-gaté par `check_artifacts` ⇒ chaque dimension recomposée du catalogue de
contrôles à chaque build), chaque entrée portant `id` + `titre` + `reference`.
L'INPUT `audit_spec.json[dimensions]` (dont le rapport DÉRIVE) sert de cohérence
croisée.
- Piège : l'audit 5D est l'audit de **SECOND NIVEAU** que l'auditeur/banquier lit.
RENOMMER un libellé (« Traçabilité & anti-invention » → …) ou PERMUTER une norme
de référence (D3 fiscal citant la mauvaise `Ley 32-23`, D2 AML la mauvaise
`Ley 155-17`, D1 probatoire `ISA 501` au lieu d'`ISA 500`) laisse le compte à 5 —
le bloc de comptage reste AVEUGLE — pendant que l'artefact dit autre chose ⇒ une
AFFIRMATION de conformité fausse (l'invention même que #6 interdit). Aucune suite
`tests/` (qui teste des FONCTIONS de contrôle, pas la prose) n'attrape ce « vert
trompeur ».
- **État courant** : **aucune dimension périmée** — les 5 triplets recoupent
l'artefact exactement (anti-invention #6, rien à réécrire). Le défaut est la
**surface ungated**.
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « Audit 5D (3e surface du
MÊME README) · IDENTITÉ des 5 DIMENSIONS » après le bloc open items) : on RECOMPUTE
la liste ORDONNÉE `{id:(titre, reference)}` depuis `audit_report.dimensions[]` (zéro
duplication du modèle de l'auditeur) et on exige que la table README l'énumère
EXACTEMENT : (1) identité par **ENSEMBLE d'`id`** (absent ET en trop mordus) ; (2)
**ORDRE** préservé (D1→D5) ; (3) `(titre, reference)` **par ligne**. Regex de ligne
`| **Dn** | … | … |` (gras + PAS de sous-point ⇒ distingue des lignes open items
`| D1.1 · … |`). Cohérence croisée bonus : `audit_report.dimensions` ==
`audit_spec.json[dimensions]` (l'INPUT dont le rapport dérive · mord un builder qui
renommerait/permuterait une dimension en aval du spec). Table disparue = échec
(traçabilité #6).
**8 morsures vérifiées** (5 côté README · 1 réordonnancement · 2 côté artefact/spec) :
README renomme D1 titre (`& anti-invention→et non-invention`) · README permute la
norme D1 (`ISA 500→ISA 501`) · README permute la norme fiscale D3 (`Ley 32-23→Ley
33-23`, affirmation de conformité fausse) · README supprime la ligne D5 (absent) ·
README réordonne D1↔D2 (ORDRE cassé) · **artefact** renomme le titre D2 (README périmé
+ divergence spec↔rapport) · **spec** renomme la référence D4 (divergence spec↔rapport)
· README supprime la table entière (INTROUVABLE, énumère `[]`) ; restauré = green :
5 dimensions `[D1..D5]` (id→titre→reference) == artefact · ordre == artefact ·
spec == rapport · exit 0. Working tree byte-restauré (`git checkout --`, **JAMAIS**
`git clean`, interdit absolu) · **7 gates re-verts** (`check_readme_claims`/
`check_docs`/`guard_constraints`/`check_ci_integrity`/`check_artifacts`/
`check_regression`/`validate_json`).
- `ci/README.md` (recap `check-readme-claims` + paragraphe détaillé « 3e surface du
MÊME README de l'audit 5D · IDENTITÉ des 5 dimensions ») 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 confirmation
des 4 open items réglementaires reste côté métier/VPS).
- **Auto-score 4Big** : 96/100.
+64 -1
View File
File diff suppressed because one or more lines are too long
+235
View File
@@ -514,6 +514,78 @@ if criteria is not None and a4_txt:
bad(f"README audit_4big · archétype `{a}` — {A4} exclut {sorted(got)} MAIS "
f"le référentiel exclut {exp} → régénérer le README")
# ============================================================================
# README de l'auditeur 4Big · SEUILS STRUCTURELS de la colonne « Fait vérifié »
# (min_doc_bytes / min_test_methods) — 3e surface du MÊME README, jamais gatée.
# ----------------------------------------------------------------------------
# Le bloc BARÈME ci-dessus recompute les POIDS (2e colonne de la table
# « Critère | Poids | Fait vérifié »), mais la 3e colonne « Fait vérifié » NOMME
# les DEUX seuils structurels que le scorer applique réellement :
# • DOC « un *.md (README/SPEC) ≥ 400 octets » → thresholds.min_doc_bytes
# • TESTS « ≥ 8 méthodes def test_* dans tests/ » → thresholds.min_test_methods
# Ces deux nombres sont DATA-DERIVED de quality_spec.json[thresholds] — l'INPUT
# du générateur, byte-gaté par check_artifacts qui prouve que quality_report.json
# se rebuild depuis lui — et TRANSCRITS À LA MAIN dans la table. AUCUN gate ne
# les liait : le bloc BARÈME ne capture que le poids (`| \`DOC\` | 20 |`),
# AVEUGLE à la 3e cellule. Élever min_test_methods (8→12 : un module de 8-11
# tests tomberait sous FAIL — la barre même que le critère pose) ou changer
# min_doc_bytes dans le spec rebuild le report SANS TOUCHER au README → prose
# « ≥ 8 méthodes » / « ≥ 400 octets » PÉRIMÉE en silence, l'auditeur documentant
# un barème qu'il n'applique plus (l'invention #6 interdit). Aucune suite tests/
# (qui teste des FONCTIONS de scoring, pas la prose) n'attrape ce « vert
# trompeur ». CROSS-CHECK d'ancrage : l'evidence TESTS de CHAQUE module du report
# porte « (seuil N) » (émise par crit_tests) — on exige N == spec.min_test_methods
# pour TOUS, prouvant que le seuil ÉCRIT dans le spec == le seuil réellement
# APPLIQUÉ à la construction (mord une divergence spec↔report). Un claim absent
# échoue AUSSI (traçabilité #6).
# ============================================================================
try:
_a4_thr = load("qa/audit_4big/quality_spec.json")["thresholds"]
min_doc = int(_a4_thr["min_doc_bytes"])
min_test = int(_a4_thr["min_test_methods"])
except (OSError, KeyError, ValueError, TypeError) as e:
bad(f"README audit_4big seuils · quality_spec.json[thresholds] illisible/inattendu : {e}")
min_doc = min_test = None
if min_doc is not None and criteria is not None and a4_txt:
# cross-check d'ancrage : seuil TESTS APPLIQUÉ (evidence report) == spec
ev_seuils = set()
for m in qr_crit["modules"]:
for ck in m.get("checks", []):
if ck.get("criterion") == "TESTS":
mm = re.search(r"\(seuil (\d+)\)", ck.get("evidence", ""))
if mm:
ev_seuils.add(int(mm.group(1)))
if not ev_seuils:
bad("README audit_4big seuils · aucune evidence TESTS « (seuil N) » dans le "
"report — impossible d'ancrer le seuil réellement appliqué")
elif ev_seuils != {min_test}:
bad(f"README audit_4big seuils · seuil TESTS APPLIQUÉ (evidence report) "
f"{sorted(ev_seuils)} ≠ spec.min_test_methods ({min_test}) → spec↔report "
f"divergent (régénérer le report)")
else:
good(f"README audit_4big seuils · seuil TESTS appliqué (report) == spec "
f"({min_test}) sur tous les modules")
# prose · DOC « ≥ N octets » (3e cellule de la ligne DOC)
md = re.search(r"`DOC`[^|]*\|[^|]*\|[^|]*?≥\s*(\d+)\s*octets", a4_txt)
if md is None:
bad(f"README audit_4big seuils · ligne DOC « ≥ N octets » INTROUVABLE dans {A4} "
f"(attendu {min_doc})")
elif md.group(1) == str(min_doc):
good(f"README audit_4big seuils · DOC — « ≥ {md.group(1)} octets » == spec ({min_doc})")
else:
bad(f"README audit_4big seuils · DOC — {A4} dit « ≥ {md.group(1)} octets » MAIS "
f"spec.min_doc_bytes == {min_doc} → régénérer le README")
# prose · TESTS « ≥ N méthodes » (3e cellule de la ligne TESTS)
mt = re.search(r"`TESTS`[^|]*\|[^|]*\|[^|]*?≥\s*(\d+)\s*méthodes", a4_txt)
if mt is None:
bad(f"README audit_4big seuils · ligne TESTS « ≥ N méthodes » INTROUVABLE dans {A4} "
f"(attendu {min_test})")
elif mt.group(1) == str(min_test):
good(f"README audit_4big seuils · TESTS — « ≥ {mt.group(1)} méthodes » == spec ({min_test})")
else:
bad(f"README audit_4big seuils · TESTS — {A4} dit « ≥ {mt.group(1)} méthodes » MAIS "
f"spec.min_test_methods == {min_test} → régénérer le README")
# ============================================================================
# Fiches agents · comptes de tests PAR SUITE (colonne « Tests » des tables) —
# même classe de « vert trompeur », au grain le PLUS FIN.
@@ -2019,6 +2091,82 @@ if a5_oi_exp is not None:
good(f"Audit 5D · énumération open items {sorted(a5_exp_set)} "
f"(control→dimension→propriétaire) == audit_report.open_items")
# ============================================================================
# Audit 5D (3e surface du MÊME README) · IDENTITÉ des 5 DIMENSIONS — le libellé
# (`titre`) ET la NORME de référence (`reference`) de chaque dimension du cadre —
# même classe de « vert trompeur », transcrite À LA MAIN dans un tableau.
# ----------------------------------------------------------------------------
# Le 1er bloc audit_5d gate le COMPTE de dimensions (« 5 dimensions (5D) ») — un
# WILDCARD AVEUGLE à QUELLE dimension : renommer « Traçabilité & anti-invention »
# ou permuter une norme de référence (D3 fiscal citant la mauvaise Ley, D1
# probatoire citant `ISA 501` au lieu d'`ISA 500`) laisse le compte à 5 pendant que
# l'artefact dit autre chose. La table « ## Les 5 dimensions » du README
# (05_deliverables_mvp/qa/audit_5d/README.md) énumère PAR LIGNE le triplet
# (`id`, `titre`, `reference`) alors que audit_report.dimensions[] (byte-gaté par
# check_artifacts ⇒ faisant autorité, chaque dimension recomposée du catalogue de
# contrôles à chaque build) fait foi. C'est l'audit de SECOND NIVEAU que
# l'auditeur/banquier lit : une norme de référence périmée (D2 AML citant une
# mauvaise Ley 155-17, D3 fiscal la mauvaise Ley 32-23) est une AFFIRMATION de
# conformité fausse — exactement l'invention que #6 interdit, qu'aucune suite
# tests/ (qui teste des FONCTIONS de contrôle, pas la prose) n'attrape. On RECOMPUTE
# la liste ORDONNÉE {id:(titre, reference)} depuis audit_report.dimensions[] (zéro
# duplication du modèle de l'auditeur · #6) et on exige que la table README
# l'énumère EXACTEMENT : identité par ENSEMBLE d'`id` (absent ET en trop mordus),
# ORDRE préservé (D1→D5), puis (titre, reference) par LIGNE. Même patron que
# l'énumération des open items ci-dessus. Cohérence croisée en bonus : les
# dimensions du rapport == celles de audit_spec.json (l'INPUT dont le rapport
# dérive) — mord un builder qui renommerait/permuterait une dimension en aval du
# spec. Un claim absent (table disparue) échoue AUSSI (traçabilité #6).
# ============================================================================
try:
a5d_dim = load("qa/audit_5d/out/audit_report.json")["dimensions"]
a5d_exp = {d["id"]: (d["titre"], d["reference"]) for d in a5d_dim}
a5d_order = [d["id"] for d in a5d_dim]
a5d_spec = load("qa/audit_5d/audit_spec.json")["dimensions"]
a5d_spec_map = {d["id"]: (d["titre"], d["reference"]) for d in a5d_spec}
if a5d_spec_map != a5d_exp:
bad(f"Audit 5D · incohérence artefacts : audit_spec dimensions {sorted(a5d_spec_map)} "
f"divergent du rapport {sorted(a5d_exp)} (titre/reference) → le rapport doit "
f"dériver du spec")
except (OSError, KeyError, ValueError) as e:
bad(f"Audit 5D · dimensions illisibles/inattendues : {e}"); a5d_exp = None
if a5d_exp is not None:
try:
a5d_lines = open(f"{D}/qa/audit_5d/README.md",
encoding="utf-8").read().splitlines()
except OSError as e:
bad(f"Audit 5D · README illisible : {e}"); a5d_lines = None
if a5d_lines is not None:
# ligne de table = « | **D1** | <titre> | <reference> | » (gras + PAS de
# sous-point ⇒ distingue des lignes open items « | D1.1 · … | »)
a5d_row_re = re.compile(
r"^\|\s*\*\*(D\d+)\*\*\s*\|\s*([^|]+?)\s*\|\s*([^|]+?)\s*\|\s*$")
a5d_seen, a5d_seen_order = {}, []
for ln in a5d_lines:
m = a5d_row_re.match(ln)
if m:
a5d_seen[m.group(1)] = (m.group(2).strip(), m.group(3).strip())
a5d_seen_order.append(m.group(1))
a5d_exp_ids, a5d_got_ids = set(a5d_exp), set(a5d_seen)
if a5d_got_ids != a5d_exp_ids:
bad(f"Audit 5D · IDENTITÉ des dimensions — table README énumère "
f"{sorted(a5d_got_ids)} MAIS audit_report dit {sorted(a5d_exp_ids)} "
f"→ régénérer le README")
elif a5d_seen_order != a5d_order:
bad(f"Audit 5D · IDENTITÉ des dimensions — ORDRE README {a5d_seen_order} "
f"MAIS audit_report dit {a5d_order} → régénérer le README")
else:
a5d_mism = [i for i in a5d_order if a5d_seen[i] != a5d_exp[i]]
if a5d_mism:
for i in a5d_mism:
bad(f"Audit 5D · dimension {i} — README dit "
f"(titre={a5d_seen[i][0]!r}, reference={a5d_seen[i][1]!r}) MAIS "
f"audit_report dit (titre={a5d_exp[i][0]!r}, "
f"reference={a5d_exp[i][1]!r})")
else:
good(f"Audit 5D · IDENTITÉ des 5 dimensions {a5d_order} "
f"(id→titre→reference) == audit_report.dimensions")
# ============================================================================
# DevOps · run-book de déploiement VPS (phases ordonnées / confirmations
# préalables) — même classe de « vert trompeur », transcrite À LA MAIN dans
@@ -5633,6 +5781,93 @@ if cm_persona is not None and chat_spec is not None:
else:
good("Chat OTOIA · (e) oracle du test persona/capabilities == CLAUDE.md")
# ============================================================================
# Fiches agents · le PIED DE PAGE canonique « Voix Amélie QC (multilingual_v2) »
# répété dans CHAQUE fiche 03_agents/*/AGENT.md — ANCRÉ sur CLAUDE.md
# §Architecture cible, jamais gaté.
# ----------------------------------------------------------------------------
# 11 des 13 fiches agents (toutes sauf faisabilite/publiciste, qui n'orchestrent
# pas OTOIA) se terminent par la ligne « - Voix <persona> <qualif> (<voix>) pour
# toute interaction OTOIA ». Elle RECOPIE À LA MAIN les TROIS tokens de la
# contrainte CLAUDE.md §Architecture cible « Voix Amélie QC (multilingual_v2) » :
# le prénom de la persona (`Amélie`), le qualificatif de marque (`QC`) et le
# modèle de voix ElevenLabs (`multilingual_v2`). Le bloc « Chat OTOIA · IDENTITÉ
# de la persona » (ci-dessus) RE-DÉRIVE bien persona+voix de CLAUDE.md, MAIS ne
# confronte QUE le module chat_otoia (spec + out/ + son propre README + l'oracle
# du test) — AVEUGLE aux 11 pieds de page des fiches. L'autre bloc (« Chat OTOIA ·
# IDENTITÉ des configs runtime ») note même explicitement que le « QC » est
# « gardé en littéral — non gaté ici ». Aucune suite tests/ (FONCTIONS du builder,
# jamais la prose des fiches) n'attrape ce « vert trompeur ».
# Piège #6 : le jour où ElevenLabs déprécie `multilingual_v2` (→ `multilingual_v3`)
# ou qu'on renomme la persona/le qualificatif dans CLAUDE.md, ces 11 pieds de page
# annonceraient EN SILENCE une voix/persona que le mandat ne prescrit plus, chaque
# fiche affirmant orchestrer « toute interaction OTOIA » avec le mauvais modèle.
# On RE-DÉRIVE le triplet (persona · qualif · voix) de CLAUDE.md §Architecture cible
# (source unique · zéro duplication — mêmes `nom`/`voix` que le bloc persona
# ci-dessus, plus le qualificatif jusqu'ici jamais ancré) et on exige de CHAQUE
# fiche portant le pied de page que ses trois tokens == CLAUDE.md. Direction
# fiche→CLAUDE.md. La disparition TOTALE du pied de page échoue AUSSI (le
# recensement qui s'évapore est lui-même une régression #6). État courant : aucune
# valeur périmée.
# ============================================================================
if True:
# RE-DÉRIVATION INDÉPENDANTE de CLAUDE.md (ne dépend PAS du parse persona
# ci-dessus, dont la regex fige « QC » en dur : un renommage du qualificatif
# doit mordre ICI, pas seulement via l'INTROUVABLE du bloc persona).
try:
_cm_af = open("CLAUDE.md", encoding="utf-8").read()
# triplet canonique : « Voix <nom> <qualif> (<voix>) » (qualif jamais figé en dur)
_maf = re.search(r"Voix\s+(\S+)\s+(\S+)\s*\(([^)]+)\)", _cm_af)
if _maf is None:
raise ValueError("« Voix <persona> <qualif> (<voix>) » INTROUVABLE "
"dans CLAUDE.md §Architecture cible")
af_nom, af_qual, af_voix = (_nfc(_maf.group(1)), _nfc(_maf.group(2)),
_nfc(_maf.group(3)))
# cohérence avec le bloc persona quand il a pu parser (zéro duplication —
# même source, mêmes valeurs) ; s'il a échoué (cm_persona None), on continue.
if cm_persona is not None and (af_nom != cm_persona["nom"]
or af_voix != cm_persona["voix"]):
raise ValueError(f"triplet {af_nom!r}/{af_voix!r} incohérent avec le bloc "
f"persona {cm_persona} (parse divergent de CLAUDE.md)")
except (OSError, ValueError) as e:
bad(f"Fiches agents · pied de page voix CLAUDE.md §Architecture cible "
f"illisible/inattendu : {e}")
af_nom = None
if af_nom is not None:
_fiches = subprocess.run(["git", "ls-files", "03_agents/*/AGENT.md"],
capture_output=True, text=True).stdout.split()
_foot_re = re.compile(
r"Voix\s+(\S+)\s+(\S+)\s*\(([^)]+)\)\s+pour toute interaction OTOIA")
af_seen = 0
for _fi in _fiches:
try:
_ftxt = open(_fi, encoding="utf-8").read()
except OSError as e:
bad(f"Fiches agents · {_fi} illisible : {e}"); continue
_mfoot = _foot_re.search(_nfc(_ftxt))
# Fiche sans OTOIA (faisabilite/publiciste) : pas de pied de page attendu.
if _mfoot is None:
if "interaction OTOIA" in _ftxt:
bad(f"Fiches agents · {_fi} — prose « interaction OTOIA » présente "
f"MAIS pied de page « Voix <nom> <qualif> (<voix>) » malformé/"
f"INTROUVABLE (attendu {af_nom} {af_qual} ({af_voix}))")
continue
af_seen += 1
got = (_nfc(_mfoot.group(1)), _nfc(_mfoot.group(2)), _nfc(_mfoot.group(3)))
want = (af_nom, af_qual, af_voix)
if got == want:
good(f"Fiches agents · {_fi} — pied de page « Voix {got[0]} {got[1]} "
f"({got[2]}) » == CLAUDE.md §Architecture cible")
else:
bad(f"Fiches agents · {_fi} — pied de page dit « {got[0]} {got[1]} "
f"({got[2]}) » MAIS CLAUDE.md §Architecture cible dit « {want[0]} "
f"{want[1]} ({want[2]}) » → persona/qualif/voix périmé (mettre la "
f"fiche à jour)")
if af_seen == 0:
bad("Fiches agents · AUCUNE fiche ne porte le pied de page « Voix "
f"{af_nom} {af_qual} ({af_voix}) pour toute interaction OTOIA » — la "
f"surface d'identité voix OTOIA s'est évaporée (régression #6)")
# ============================================================================
# Mobile · le SDK MAJEUR Expo (« Expo 54 ») — ANCRÉ sur la roadmap Sprint 5 l.56,
# transcrit À LA MAIN dans le README sans AUCUN gate d'IDENTITÉ ni d'ancrage.