Compare commits
4 Commits
85a9ba7fca
...
d1e96d7784
| Author | SHA1 | Date | |
|---|---|---|---|
| d1e96d7784 | |||
| e874717f0f | |||
| b9e333ceef | |||
| a52daa056f |
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
File diff suppressed because one or more lines are too long
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user