Preuve · rapport

liste-23-constats-reconstituee-2026-09-13.md

Rapport de vérification de l'audit, texte brut · sur GitHub (dépôt privé, accès requis)

187 lignes · ← retour à l'audit · catalogue des preuves

1# Liste des 23 constats de `audit-v3.html` — reconstitution
2
3**Date :** 13/09/2026 · **Régime :** lecture seule, aucun appel réseau, aucune clé. Écriture dans `analyse/` uniquement.
4**Nature du document :** extraction factuelle. Aucun constat n'est jugé, recoupé ni ajouté.
5
6---
7
8## ⚠ Addendum du 14/09 — le document source a été retrouvé, et cette reconstitution est corrigée sur pièce
9
10**`audit-v3.html` n'était pas perdu : il avait été publié comme artefact**, pas poussé sur GitHub. Version du 09/09 : `https://claude.ai/code/artifact/aa01982a-2537-4c53-9454-23befa9d40c4` (privé, 89 Ko, 737 lignes). **Copie sauvegardée dans `analyse/audit-v3-publie-2026-09-09.html`** — plus jamais dans un scratchpad.
11
12**Les gravités étaient récupérables** (classe `sev-b`/`sev-m`/`sev-n` **et** libellé en clair « Bloquante » / « Majeure » / « Mineure » sur chaque bloc). Extraction mécanique, **23 constats, 10 bloquantes, 12 majeures, 1 mineure — conforme au bandeau** :
13
14| n° | gravité | n° | gravité | n° | gravité |
15|---|---|---|---|---|---|
16| 1 | Bloquante | 9 | Majeure | 17 | Majeure |
17| 2 | Majeure | 10 | Bloquante | 18 | Majeure |
18| 3 | Bloquante | 11 | Bloquante | 19 | Majeure |
19| 4 | Bloquante | 12 | Bloquante | 20 | Majeure |
20| 5 | Majeure | 13 | Majeure | 21 | Majeure |
21| 6 | Bloquante | 14 | Bloquante | 22 | **Mineure** |
22| 7 | Bloquante | 15 | Bloquante | 23 | Majeure |
23| 8 | Majeure | 16 | Majeure | | |
24
25**Cinq constats portent la marque « correctif déjà écrit dans les sources »** (`fix done`) : **1, 3, 4, 15, 16** — conforme à « Dans quel ordre » (« ils couvrent cinq des vingt-trois constats »).
26
27**Sept titres de cette reconstitution étaient inexacts ou approchés.** Titres publiés, au mot :
28- **3** : « Une erreur de chargement s'affiche comme « il n'y a rien » » *(reconstitué « non retrouvé »)*
29- **5** : « Le contrôle de cohérence existe, et rien ne le déclenche » *(idem)*
30- **8** : « Le barème du commercial n'a aucun garde-fou, celui du centre en a un » *(idem)*
31- **9** : « L'explication du montant est calculée, puis jetée » *(idem)*
32- **11** : « **Deux moteurs de notation coexistent, et rien ne dit lequel tourne** » *(reconstitué « Une grille en mode malus est additionnée… » — la substitution du 09/09 a été appliquée à la publication, sous une forme différente de celle proposée)*
33- **14** : « **Un client jamais appelé** est enregistré comme injoignable » *(la substitution « Un rendez-vous… » proposée le 09/09 n'a PAS été appliquée à la publication)*
34- **17** : « **Trois portes** reçoivent les appels, une seule vérifie qui l'appelle » *(reconstitué « Deux portes d'entrée n'authentifient rien » — la contradiction « le titre dit deux, le corps trois » relevée par `relecture-coherence:32` a été résolue à la publication)*
35
36**Conséquence** : les substitutions du 09/09 ont été appliquées **partiellement** au document publié. Tout face-à-face doit se faire contre `audit-v3-publie-2026-09-09.html`, pas contre les tableaux ci-dessous.
37
38## Le document source
39
40`/private/tmp/claude-501/-Users-gregorydelaloeuf-Documents-scalewayxflexylead/ff38fb78-ccda-4574-ab16-1a49423c4d29/scratchpad/audit-v3.html` — **vérifié une fois le 13/09 : le répertoire de session n'existe plus.** Aucune copie ailleurs (`find` sur `.html` dans tout le projet : uniquement des fichiers des dépôts de code). La reconstitution repose donc entièrement sur les cinq rapports `preuves-*-2026-09-09.md` et sur `relecture-coherence-2026-09-09.md`.
41
42## Comment la numérotation a été établie
43
44Les cinq rapports numérotent **localement, par famille** ; aucun ne porte la numérotation globale 1→23. Elle se déduit du nombre de constats par famille :
45
46| Famille | Rapport | Nombre de constats | Numéros globaux |
47|---|---|---|---|
48| 1 — « Les chiffres » | `preuves-chiffres` | 5 (blocs 1 à 5) | **1 – 5** |
49| 2 — « L'argent » | `preuves-argent` | 4 (« les quatre constats de la famille », `preuves-argent-2026-09-09.md:27`) | **6 – 9** |
50| 3 — « La notation qualité » | `preuves-notation` | 4 (« ses 4 constats », `preuves-notation-2026-09-09.md:7`) | **10 – 13** |
51| 4 — « Le travail, les entrées, les traces » | `preuves-travail` | 10 (sections `## 1.` à `## 10.`) | **14 – 23** |
52
53Total **5 + 4 + 4 + 10 = 23**, conforme au bandeau (`grep -c '<div class="f">'` → 23, `preuves-fond-2026-09-09.md:30`).
54
55**Trois contrôles indépendants valident ces bornes** — `relecture-coherence` est le seul rapport à citer des numéros globaux, et les trois tombent juste :
56
57- **constat 11** : « son titre a été corrigé — il dit désormais que le moteur additionnait *avant son correctif* » (`relecture-coherence-2026-09-09.md:11`). C'est mot pour mot la substitution proposée pour le 2ᵉ constat de la famille 3 (`preuves-notation-2026-09-09.md:45`). Famille 3 commençant à 10 → 2ᵉ = **11**. ✔
58- **constat 12** : « le remède du constat 12 (“faire calculer la correction comme le moteur”) » (`:13`) = 3ᵉ constat de la famille 3 → **12**. ✔ Et `preuves-notation-2026-09-09.md:121` déclare ces deux constats « mutuellement exclusifs », exactement comme `relecture-coherence:13`.
59- **constat 17** : « Le titre du constat 17 dit deux, son corps et son remède en traitent trois » (`:32`) = 4ᵉ constat de la famille 4 → 14+3 = **17** = « Deux portes d'entrée n'authentifient rien », dont le corps commence par « Trois adresses web reçoivent… » (`preuves-travail-2026-09-09.md:68`). ✔
60
61## Avertissement sur les sources d'appui — piège des deux corpus
62
63Les fichiers du **07 et 08/09** (`audit-de-nos-releves`, `ligne-par-ligne-lot*`, `absences-verifiees`, `relecture-publiee-*`, `verif-finale-*`) vérifient un document **antérieur et différent** : 11 cartes de constat, tableaux interactifs `MODULES` / `STEPS` / `BUCKETS`, bilan « Onze défauts, dont trois bloquants » (`audit-de-nos-releves-2026-09-07.md:368`). **Aucun titre ni aucune gravité n'a été importé de ces fichiers.** En particulier, la gravité « Mineure » que le document du 07/09 donnait au contrôle de cohérence (`audit-de-nos-releves-2026-09-07.md:341`) n'est **pas** reportée sur le constat 5 de `audit-v3.html`.
64
65---
66
67# Famille 1 — « Les chiffres » (constats 1 à 5)
68
69| n° | titre du constat, au mot | gravité | sur quoi il repose | statut au 09/09 |
70|---|---|---|---|---|
71| **1** | « Personne ne peut dire combien d'appels ont été reçus » — titre confirmé, `preuves-chiffres-2026-09-09.md:29` (ligne 1.0, marquée « (titre) ») | non retrouvée | **(a) code déployé** : `backend/flexylead-app-dashboard-query.mjs:224,319,744,773,848-849,858-859` · `backend/flexylead-app-commercial-metrics-query.mjs:665` · `backend/flexylead-app-acquisition-query.mjs:174` · `backend/flexylead-app-pilotage-summary-query.mjs:3164,3304,3100-3115` · `backend/ringover-call-events.go:2002-2005,2834-2845,2676-2708`. **(f) décompte** : quatre définitions de « manqué » recomptées. **(b) source non déployée** pour les trois correctifs : `functions/flexylead-app-dashboard-query.mjs:153-157,866-867` · `functions/ringover-call-events/main.go:625-637,684-745` | **borné.** Titre substitué → « Quatre fonctions comptent les appels reçus, et aucune ne compte comme les autres » (`:29`). Corps : « trois définitions » → « quatre » si l'on dépasse les trois écrans nommés (`:30`) ; 1.2 et 1.3 tenus, 1.2 renforcé (le refus de regrouper est commenté à l'insertion et non compensé au comptage) ; 1.4 tenu tel quel |
72| **2** | « Deux compteurs justes portent le même nom » — titre confirmé, `preuves-chiffres-2026-09-09.md:34` (ligne 2.0, « (titre) ») | non retrouvée | **(a) code déployé** : `backend/flexylead-app-pilotage-remuneration-query.mjs:2177-2185,2094-2116,993-997,2171,755` · `backend/flexylead-create-sales.mjs:695-701,733-762` · `backend/flexylead-app-commercial-metrics-query.mjs:732`. **(b) source non déployée** pour le test qui exige la divergence : `functions/flexylead-app-pilotage-query.test.mjs:2488,2497,2515-2516,2453-2455`. **(b) écran, source seule** : `src/routes/PilotageProductionRoute.tsx:1363,1454,1509` · `src/main.tsx:6960,11141` | **borné sur deux phrases.** 2.3 : ajouter que le **test** n'est pas livré en production mais que le **comportement** testé est en service (`:37`). 2.4 substitué → « … et les deux chiffres puisent dans la même case, `prime_montant`, qui sert à la fois de prime du commercial et de tarif du centre » (`:38`) |
73| **3** | **titre non retrouvé.** Le rapport ne cite aucune ligne marquée « (titre) » pour ce bloc. Première phrase du constat, citée comme telle : « À onze endroits de l'interface, quand une donnée n'arrive pas, l'écran affiche zéro au lieu d'une erreur. » (`preuves-chiffres-2026-09-09.md:39`, ligne 3.1) | non retrouvée | **(b) source de travail seule pour la moitié écran** : `src/dashboard/api/client.ts:1007-1019,1023` — aucune copie déployée de `src/` n'a été livrée (`preuves-argent-2026-09-09.md:152`). **(a) code déployé pour la moitié serveur** : `backend/flexylead-app-commercial-missed-calls-query.mjs:289` · `backend/flexylead-app-pilotage-summary-query.mjs:4400,4411-4413,3018-3025,2792,3955,4057,4112,4151,4283,4319`. **(f) recomptage** : les chiffres publiés 38/10/16 ne sont reproductibles par aucun des trois motifs essayés. **(b) correctif source** : `functions/flexylead-app-commercial-resource-query.mjs:285-286,414,419` | **borné sur les quatre phrases.** 3.1 : « le chiffre est juste, la description est fausse » → substitution (onze **tables** dans **une** fonction, `loadFullState`, dont les barèmes et les périodes de paie). 3.2 : chiffres **non reproductibles** → substitution (« seize conversions d'erreur en résultat vide » dans la fonction unique dont sont tirées les 23 fonctions commerciales). 3.3 : vrai avec réserve → substitution. 3.4 : ajouter « Le correctif ne couvre qu'un des trois masquages de cette fonction » |
74| **4** | « Quand une requête est trop lente, la production la relance — jusqu'à douze fois » — titre confirmé, `preuves-chiffres-2026-09-09.md:43` (ligne 4.1, « (titre) ») | non retrouvée | **(a) code déployé** : `backend/flexylead-app-pilotage-summary-query.mjs:3429,106,3389-3391,3414,3439,4400-4419,4412`. **(c) plateforme Apteva** (confirme que les deux étages de délai sont distincts côté serveur) : `apteva-platform/apps/mcp/tables/types.go:330,334` · `apps/mcp/tables/schema_cache.go:211-212`. **(f) calcul** : 4 morceaux × 3 tentatives = 12. **(b) correctif source** : `functions/flexylead-app-pilotage-query.mjs:4394-4397,3391,3414-3427` | **tenu, une substitution.** 4.1 à 4.3 vrais, chiffre 12 exact. 4.4 substitué → le correctif agit sur **deux** leviers, `queryPartitionedPilotageInteractions` cédant la place à `queryReducedPilotageInteractions` (une requête au lieu de quatre). 4.5 conservé mais **assorti du niveau SUGGÉRÉ** (aucune mesure en production) |
75| **5** | **titre non retrouvé.** Aucune ligne « (titre) » pour ce bloc. Première phrase du constat, citée comme telle : « Le logiciel contient un vérificateur qui cherche les incohérences : une opportunité attribuée deux fois, un rappel dont l'appel a disparu. » (`preuves-chiffres-2026-09-09.md:48`, ligne 5.1) | non retrouvée — **et ne pas importer** la « Gravité Mineure » du document du 07/09 (voir avertissement ci-dessus) | **(a) code déployé** : `backend/flexylead-integrity-audit.mjs:9-18,19-43,44,64,72,97-110,89,90` et les cinq `LIMIT 100` (`:17,42,62,70,78`) · `backend/flexylead-app-command-dispatch.mjs:8` · `scripts/application-api-manifest.mjs:48`. **(f) recherche d'absence** : `grep` sur `integrity.audit` (10 résultats, aucun sous `src/`, `dist/`, `prebuilt-dist/`) ; `ls config/jobs/` (six tâches, aucune ne le vise). **(a) scripts** : `scripts/verify-production-reliability-contracts-20260824.mjs:39,5,7-11` · `scripts/verify-staging-action-contracts.mjs:24` | **borné sur une phrase.** 5.2 « Il est correctement écrit » substitué → « Il est écrit avec soin, mais il ne compte que les cent premières anomalies de chaque type, et il rend zéro quand il échoue » (le même geste que le constat 3). 5.1, 5.3, 5.4 tenus ; 5.4 enrichi de « et l'un comme l'autre ne lui demandent que vingt-cinq lignes : ils vérifient qu'il répond, pas ce qu'il trouve » |
76
77---
78
79# Famille 2 — « L'argent » (constats 6 à 9)
80
81**Garantie propre à cette famille :** les sept fichiers porteurs sont **identiques** entre source de travail et copie déployée (`preuves-argent-2026-09-09.md:15-27`). « C'est le seul endroit du document où cette garantie est acquise » (`:27`) : ces quatre constats décrivent ce qui tourne. Exception : les phrases d'écran (`src/`), qui reposent sur la source seule (`:152`).
82
83| n° | titre du constat, au mot | gravité | sur quoi il repose | statut au 09/09 |
84|---|---|---|---|---|
85| **6** | « Aucun montant dû à un centre n'est jamais figé » — titre confirmé, `preuves-argent-2026-09-09.md:35` (ligne 1.1, « (titre) ») | non retrouvée | **(a) code déployé** (fichiers identiques source/déployé) : `flexylead-app-pilotage-query.mjs:2264,2421,598,440,170-172,4500,2229-2236,2758,2746` · `flexylead-facturation-schema.mjs:94-116,101-103,134` · `flexylead-manage-facturation.mjs:671,746-748,733,12-19,267-283`. Confirmé sur `backend/` par `preuves-fond-2026-09-09.md:24,70` (zéro écriture dans la fonction de rémunération déployée). **(f) commandes d'absence** A, B, C : aucune fonction de rémunération de centre n'écrit ; aucune colonne de montant de centre ; le mot « centre » n'apparaît jamais comme axe de facturation. **(b) écran** : `src/routes/FacturationRoute.tsx:26` | **borné sur deux phrases.** 1.3 « avec les barèmes du jour » **FAUX** (imprécis, et l'imprécision masque un défaut) → substitution : « avec les seules règles encore actives aujourd'hui, dans leur version datée du jour de la vente… c'est `monthlyFormulaRuleFor` qui écarte l'inactif avant de lire les dates ». 1.5 borné → « états qui ne reviennent pas en arrière » faux au sens strict (`review → draft` est un retour ; l'irréversibilité ne commence qu'à `approved`). 1.1, 1.2, 1.4, 1.7 tenus, avec nommage des fonctions |
86| **7** | « Supprimer une règle de prime l'efface de tout le passé » — titre confirmé, `preuves-argent-2026-09-09.md:42` (ligne 2.1, « (titre) ») | non retrouvée | **(a) code déployé** : `flexylead-manage-offer-catalog.mjs:134-143` · `flexylead-update-sale.mjs:246-248,41-42,62-64,82,268,270,278` · `flexylead-create-sales.mjs:733-762,736,741,749,699-701` · `flexylead-app-commercial-resource-query.mjs:486-488,579` · `flexylead-app-pilotage-query.mjs:2231`. **(b) écran, source seule** : `src/main.tsx:11002` (`salePrimeLabel`) | **borné, titre compris.** Titre substitué → « **Supprimer une règle de prime la rend introuvable pour tout le passé** » : le montant déjà écrit survit, c'est la règle qui l'explique qui disparaît ; l'effacement est **différé au prochain enregistrement de la vente**. 2.3 substitué → « Rouvrir une vente ancienne **et y changer l'offre ou la nature de la vente** efface sa prime — ou bloque la modification ». 2.4 (correctif) substitué → ce n'est pas « deux lignes » mais **quatre endroits**. 2.2 tenu, littéralement vrai |
87| **8** | **titre non retrouvé.** Aucune ligne « (titre) » pour ce bloc. Première phrase du constat, citée comme telle : « Côté centre, impossible d'entrer une date passée » (`preuves-argent-2026-09-09.md:47`, ligne 3.1). Le corps oppose 3.3 : « Côté prime du commercial : date facultative, rétroactivité libre, écriture par-dessus l'ancienne valeur. » | non retrouvée | **(a) code déployé** : `flexylead-manage-production-economics.mjs:562-569,515-541` · `flexylead-manage-offer-catalog.mjs:245-250,179-181,126,209,173`. **(b) écran, source seule** : `src/dashboard/pages/Offres.tsx:699,702,414,480` — pas d'attribut `min` sur le champ de date. **(f) tests du prestataire** : `flexylead-manage-production-economics.test.mjs:362,324` · `flexylead-manage-offer-catalog.test.mjs:169-173` | **tenu, une substitution sur le correctif.** 3.1 vrai « et plus strict encore » (le jour même est refusé aussi). 3.2, 3.3, 3.4 tenus, avec nommage. 3.5 (correctif) **FAUX** : « Le modèle est à trente lignes de là, dans le même dossier » → substitution « Le modèle est dans le fichier voisin, `flexylead-manage-production-economics` » (les deux fonctions sont à 47 lignes d'écart, et la fonction à corriger est dans **un autre fichier**). À noter : `preuves-argent:75` relève que la phrase « Les barèmes sont versionnés » de « Ce qui tient » **contredit ce constat** |
88| **9** | **titre non retrouvé.** Aucune ligne « (titre) » pour ce bloc. Première phrase du constat, citée comme telle : « Pour chaque contrat d'énergie, le système établit quelle règle s'est appliquée et pourquoi. » (`preuves-argent-2026-09-09.md:52`, ligne 4.1) | non retrouvée | **(a) code déployé** : `flexylead-app-pilotage-query.mjs:2421-2440,2460-2469,2442-2458,2264,1116` ; confirmé sur `backend/flexylead-app-pilotage-remuneration-query.mjs:2264-2328,916` (`preuves-fond-2026-09-09.md:24`). **(b) écran, source seule** : `src/routes/PilotageProductionRoute.tsx:1105` (seule exception où le détail survit à l'écran) | **tenu sur les quatre phrases** (4.1 à 4.4 vrais), avec nommage des deux côtés — `evaluateSaleRemuneration` produit l'explication, `saleRemunerationForEntry` la jette. **Mais la reprise du constat dans « Ce qu'il faut retenir » (point 03) est FAUSSE** et a été substituée : « Pour chaque **vente**, la règle de prime appliquée est établie — puis jetée » → « Pour chaque **montant dû à un centre**… », car `prime_rule_id` **est** écrit sur la vente et la phrase contredisait le point « solide » du même document (`preuves-fond-2026-09-09.md:69`) |
89
90---
91
92# Famille 3 — « La notation qualité » (constats 10 à 13)
93
94**Garantie propre à cette famille :** le moteur `process-appel-db-only.mjs` est **identique** (`md5`) entre les deux livraisons et entre `functions/` et `backend/` (`preuves-notation-2026-09-09.md:17-21`). Exception décisive : le constat 11, qui dépend d'une **troisième copie** non datée.
95
96| n° | titre du constat, au mot | gravité | sur quoi il repose | statut au 09/09 |
97|---|---|---|---|---|
98| **10** | « Le texte de l'appel ne dit pas qui parle, et onze contrôles y cherchent des phrases » — titre confirmé, `preuves-notation-2026-09-09.md:37` (ligne #1, « (titre) ») | non retrouvée | **(a) code déployé** : `process-appel-db-only.mjs:945-958,951-957,982-993,1005,2419-2628,2448,2453,2458,2463,2468,2488-2493,2536-2538,2548,2555,2574,2371` · `backend/flexylead-transcribe-telephony-recording.mjs:200-207,213-214,151`. **(c) plateforme Apteva** : `apteva-platform/integrations/src/apps/deepgram.json` (`response_omit` sur les mots) · `apteva-platform/server/connections.go:2579-2582,1954`. **(f) recomptage** : onze sondes exactement ; 33/100 sur `mint_energie_v2`, 25/100 sur `conso_pilote_v2`, recalculés depuis `config/*.json`. **Réserve du rapport** : ces barèmes sont « des chiffres de configuration livrée, pas des chiffres relevés en production » — les fichiers `config/*.json` n'ont **aucun chargeur dans le dépôt** (`:114`) | **tenu.** #1, #3, #4, #5, #6, #7, #8 vrais. #2 précisé → la seconde voie (`flexylead-transcribe-telephony-recording`) demande bien la séparation des interlocuteurs mais n'en lit que le texte brut ; dans les deux cas la colonne écrite ne porte aucune étiquette. Ne pas généraliser : sur `free_v3` et les deux grilles Selectra l'exposition est **de zéro point** (`:122`) |
99| **11** | « Une grille en mode malus est additionnée au lieu d'être déduite » — titre confirmé, `preuves-notation-2026-09-09.md:45` (ligne #9, « (titre) ») | non retrouvée | **(b) copie de moteur livrée, non datée, non identifiée comme déployée** : `functions/process-appel-db-only.prod-active-before-prompt-fix.mjs` (2 338 lignes, somme des scores en `:1795-1796`, `grep -c` = 0 sur `calculateEvaluationScore`, `normalizeScoringMode`, `base_score`) — **contre (a) code archivé comme déployé**, qui connaît le mode malus : `backend/process-appel-db-only.mjs:1233-1256,1244-1256`. **(e) déclaration de Marco** : c'est le `README.md:11,156,158` qui présente `backend/` comme l'archive du déployé. **(f) recalcul** des barèmes cumulés : 69,5 et 14. **(a) grilles** : `config/hop_selectra_quality_v1.json` · `config/hop_selectra_quality_v2.json:75-76` | **le plus touché des 23 — et non publiable en l'état.** Titre borné → « Une grille en mode malus est additionnée **par la correction manuelle** — et l'était par le moteur **avant son correctif** ». #10 « Une des grilles » → **« Deux des grilles »**. #11 **FAUX en l'état** → substitution en question ouverte à Marco : « Rien dans la livraison ne dit laquelle tourne… cela commande le sort de toutes les notes Selectra déjà rendues ». #12 **FAUX** (« 40 sur 50 » : aucune grille du dépôt n'a un barème cumulé de 50) → substitution par 59,5/69,5 et 10/14. #13 (correctif) conditionné à #11. **`relecture-coherence-2026-09-09.md:11` : après substitution le constat est resté contradictoire avec lui-même** — titre corrigé, corps inchangé — et c'est « le point bloquant avant toute publication ». C'est aussi le seul des 23 où le document s'écarte de sa propre lecture de `backend/` comme production (`:19`) |
100| **12** | « Corriger une note à la main la fait tomber sous le seuil de paiement » — titre confirmé, `preuves-notation-2026-09-09.md:50` (ligne #14, « (titre) ») | non retrouvée | **(b) écran, source seule** : `src/dashboard/components/RapportViewer.tsx:125,118-121,110-112,151-165,208` · `src/dashboard/api/client.ts:7210-7219` · `src/dashboard/lib/grilleScoring.ts:3-9` (sait faire le malus, n'est pas appelée) · `src/dashboard/lib/scoring.ts:10-17`. **(a) code déployé** pour le seuil et l'exclusion : `backend/flexylead-app-pilotage-remuneration-query.mjs:2412-2419,2421-2440,2391,2380-2397,2577-2597` · `backend/flexylead-admin-data-mutation.mjs:21` · `backend/flexylead-manage-production-economics.mjs:362-372,329`. **(b) tests** : `functions/flexylead-app-pilotage-query.test.mjs:1783-1787` (règle « Vente non rémunérée sous 80/100 »), `:1684-1685` (−4 € sous 85). **(f) recalcul** : 100 → 69,5 et 100 → 14 ; conséquence chiffrée −68 € par contrat corrigé | **tenu — c'est le constat que le contre-audit retient des deux (`:121`).** #14, #16, #18, #19 vrais. #15 borné → « il garde le verdict et le score d'origine **de chaque ligne** — mais pas la note globale d'origine, qui est écrasée ». #17 borné → « … dès que la grille fonctionne par malus. L'écran possède pourtant la fonction qui sait le faire, `grilleScoringSummary` ; l'écran de correction ne l'appelle pas ». #18 enrichi : **la seule modification d'un commentaire suffit** à déclencher le recalcul. #20 borné → le seuil n'est **pas dans le code**, c'est une donnée de la formule de rémunération du centre. **`relecture-coherence:13` : son remède propagerait le défaut si le constat 11 tenait ; les deux ne peuvent pas être publiés ensemble en l'état** |
101| **13** | « Le même appel peut recevoir deux notes différentes » — titre confirmé, `preuves-notation-2026-09-09.md:60` (ligne #24, « (titre) ») | non retrouvée | **(a) code déployé** : `process-appel-db-only.mjs:2016-2041,2021-2026,2823,2919,2940-2944,2193-2234,3404,3565-3585,2272-2295,2133,3512-3534,3510,3777` · `backend/flexylead-app-pilotage-remuneration-query.mjs:2380-2397,2577-2597`. **(f) recherches d'absence** : zéro `temperature`/`seed`/`top_p`/`top_k` ; zéro `corrected_at`/`corrected_by`/`ai_verdict`/`ai_score` dans le moteur ; aucune colonne de modèle ni de prompt dans la table `rapports`. **(b) écran** : `src/dashboard/lib/attempts.ts` · `RapportViewer.tsx:151-157`. **(a) colonnes existantes mais remplies seulement par les semeurs** : `functions/test-support/table-schema.json:537,542` · `scripts/seed-local-heavy-production.mjs:259` · `scripts/seed-staging-*.mjs:114-115` | **tenu, une substitution.** #21, #22, #23, #24, #26, #28 vrais. #25 enrichi du levier → « aucune température ni graine n'est transmise à `claudeEvaluate` ». #27 **borné** : « Deux notes qui se contredisent ne peuvent pas être départagées » → « **Le calcul retient d'office la plus récente.** Faute d'avoir gardé le modèle et les instructions, personne ne peut dire si c'est la bonne » — le départage existe et il est mécanique |
102
103---
104
105# Famille 4 — « Le travail, les entrées, les traces » (constats 14 à 23)
106
107Titres pris aux intitulés de section de `preuves-travail-2026-09-09.md` (`## 1.` à `## 10.`), verbatim.
108Conventions du rapport : **D1** = `code/flexylead-crm/backend/` (déployé), **D2** = `code/flexylead-crm-0908/backend/` (mêmes captures + 10 ajouts), **S** = `functions/` (sources de travail non déployées), **P** = plateforme Apteva.
109
110| n° | titre du constat, au mot | gravité | sur quoi il repose | statut au 09/09 |
111|---|---|---|---|---|
112| **14** | « Un client jamais appelé est enregistré comme injoignable » — `preuves-travail-2026-09-09.md:21` et ligne `:25` (« (titre) ») | non retrouvée | **(a) code déployé** : D1 `flexylead-route-work-items.mjs:1040-1066,1046,1059-1060,1042-1044,47-49` · D1 `flexylead-record-work-result.mjs:150` · D1 `flexylead-app-dashboard-query.mjs:87` · D1 `flexylead-app-commercial-ai-operations-query.mjs:1585`. **(f) recherche d'absence** : seuls deux fichiers écrivent `no_show`/`unreachable` | **borné, titre compris.** Titre → « Un **rendez-vous** jamais appelé est enregistré comme injoignable ». « Un lead ou un rendez-vous » **FAUX pour le lead** (la requête filtre `reservation_type = 'ai_booking'` : ni les leads, ni les rappels planifiés, ni les appels manqués) → « Un rendez-vous pris par l'agent vocal et non distribué dans la journée ». Preuve **plus forte que le texte** : deux fonctions écrivent la même valeur `unreachable`, l'expiration automatique et le NRP réellement constaté, et rien ne les distingue en base. À ajouter si le document veut être opposable : l'expiration ne tourne que dans un passage de maintenance |
113| **15** | « La relance d'un numéro qui n'a pas répondu est morte » — `preuves-travail-2026-09-09.md:37` | non retrouvée | **(a) code déployé** : D1 `flexylead-start-callback.mjs:619` · D1 `flexylead-record-work-result.mjs:3` (`MAX_NRP = 4`). **(b) source non déployée** (les trois moitiés du correctif) : S `flexylead-record-work-result.mjs:137,166-168` · S `flexylead-start-callback.mjs:620-634` (`nrpWaiting`, 0 occurrence en D1/D2) · S `flexylead-claim-missed-call.mjs:845-870` (`recoverFailedCallbackPointer`, 0 en D1/D2) | **borné sur le compte.** « Deux fonctions, un seul défaut » → **trois** : `buildTransition` ne libère pas la fiche, `assertActiveMissedCallClaim` refuse alors le rappel suivant, `recoverFailedCallbackPointer` n'existe que dans les sources. « Les deux moitiés du correctif » → « Les **trois** moitiés ». Le reste tenu ; première phrase précisée (le moment exact est l'enregistrement d'un résultat NRP, pas la fin du rappel) |
114| **16** | « Un appel pris par le serveur vocal est compté comme un appel pris par un commercial » — `preuves-travail-2026-09-09.md:50` | non retrouvée | **(a) code déployé** : D1 `flexylead-app-dashboard-query.mjs:139-148` · D1 `flexylead-app-acquisition-query.mjs:10-18` · D1 `ringover-call-events.go:611,2912-2918`. **(b) source non déployée** : huit fichiers + un module neuf `functions/shared/ai-callback-obligation.mjs` ; S `flexylead-app-dashboard-query.mjs:142`, S `flexylead-app-acquisition-query.mjs:13`, S `flexylead-route-work-items.mjs:150,159,1220`, S `flexylead-claim-missed-call.mjs:548`, S `ringover-call-events/main.go:3030-3042`. **(d) message WhatsApp** pour la phrase de datation — **hors code** | **borné — « la règle qui coûte le plus cher au document » (`:225`).** « **Rien, en production, ne distingue les deux » est FAUX** : la marque existe déjà en production (`isUnattributedIVRAnswer` écrit `handled_by="ivr"`) → substitution « La marque existe en production… Aucun compteur ne la lit ». « Cinq fichiers » → **huit, plus un module neuf**. « C'est sa réponse aux appels mal attribués trouvés le 7 septembre » : **hors code**, à sourcer sur le message et non sur le code. Le reste tenu |
115| **17** | « Deux portes d'entrée n'authentifient rien » — `preuves-travail-2026-09-09.md:64` | non retrouvée | **(a) code déployé** : D1 `flexylead-ringover-smart-routing.mjs:30,2862-2894,118,2087,2599` · D1 `ringover-call-events.go:2772,2950,1798,1864,1262,1285-1293,1378,1456,623,659` · D1 `flexylead-project-telephony-event.mjs:686-692,532-533` · `scripts/application-api-manifest.mjs:71`. **(b) source** : S `ringover-call-events/main.go:29-35,31,3269-3310` ; les **4 565 lignes** publiées sont celles de la source (le déployé en fait **4 440**). **(f) greps d'absence** : `.Headers` → 0 partout ; aucun `os.Getenv` ; aucune trace d'authentification dans la troisième porte | **borné, et une contradiction interne non résolue.** « Trois adresses web » borné : deux portes publiquement joignables de façon établie, la troisième non prouvée (absente du manifeste). « 4 565 lignes » est le chiffre de la source → substitution mentionnant les **4 440 lignes en service**. « Refuser le mode non chiffré » **non prouvable ici** (relève de la console Ringover, hors des trois dépôts) → à retirer ou à dire explicitement. **`relecture-coherence:32` : « Le titre du constat 17 dit deux, son corps et son remède en traitent trois »** — contradiction restée ouverte. Les phrases sur ce que la porte peut écrire sont toutes tenues, et celle sur l'aiguillage renforcée (`presence_status` écrit, `availability_status` lu) |
116| **18** | « Le classement énergie ou télécom est une devinette, et elle penche toujours du même côté » — `preuves-travail-2026-09-09.md:82` | non retrouvée | **(a) code déployé** : D1 `leadbyte-event-inspector.mjs:360-373,364-368,369-370,1026-1044,1041-1042,375-379,931-942` · D1 `flexylead-app-commercial-bootstrap-query.mjs:1484-1493` · D1 `flexylead-app-dashboard-query.mjs:1130-1132` · D1 `flexylead-sync-local-google.mjs:485-486`. **(b) source non déployée** : S `flexylead-sync-local-google.mjs:2921-2940,3136` (`preciseTelecomOfferId`). **(f) test** : S `flexylead-manage-production-economics.test.mjs:144-153`. **(b) écran** : `src/dashboard/lib/dispatch.ts:3,83` · `src/main.tsx:811,2893` (lectures seules) | **borné sur deux phrases.** Le cœur tenu : l'énergie est testée en premier, l'erreur va toujours du télécom vers l'énergie. « Un lead mal classé est refusé au fournisseur » **à borner** — aucun chemin de refus dont la cause soit le classement n'a été trouvé → « part dans la mauvaise file d'attribution ». « En production, une vente d'une offre télécom précise n'est classée ni en télécom ni en énergie » **FAUX / non reproduit** → substitution par le fait voisin établi : les offres télécom précises n'existent pas au catalogue déployé, et une prime visant `free_box_pop` est **écartée du calcul**. « Bouton de reclassement » vrai, à borner : le seul reclassement possible porte sur la **campagne**, pas sur le lead |
117| **19** | « Le consentement du client n'est rangé nulle part » — `preuves-travail-2026-09-09.md:95` | non retrouvée | **(a) code déployé** : D1 `leadbyte-event-inspector.mjs:343-358,1011-1024,1085-1087,1108-1110`. **(f) recherche d'absence** : aucune colonne, aucun champ `consent`/`optin` dans les deux dépôts ; la table `prospects` (34 colonnes) n'en comporte aucune. **(d)/métier** pour « Le client donne trois accords » — **hors code**, donnée du formulaire LeadByte | **tenu et renforcé.** « Les trois traversent le système sans jamais entrer dans une case qu'on puisse relire » vrai. « Au mieux noyés dans un champ de notes tronqué » → substitution **plus dure** : `isSensitiveKey` supprime tout champ dont le nom contient « email » ou « phone », donc l'accord courriel et l'accord téléphonique n'atteignent même pas les notes — **seul l'accord SMS peut survivre**. « Compter d'abord combien de fiches n'ont aucun accord » **à corriger** → « Il n'y a rien à compter : aucune fiche n'a d'accord enregistré » ; la mesure utile est autre |
118| **20** | « Des enregistrements finissent par être introuvables » — `preuves-travail-2026-09-09.md:107` | non retrouvée | **(a) code déployé** : D1 `ringover-audio-mirror.mjs:4-14,11,65` · D1 `ringover-audio-reconcile.mjs:8-30,261,255-259,299,456-478` · `config/jobs/ringover-audio-reconcile.json`. **(f) recherche d'absence** : `ringover-audio-mirror` n'a **qu'une** occurrence dans tout le dépôt, son propre test — absent du manifeste, des six tâches et des cinq flux ; aucune fonction ne remet `storage_status` à zéro | **tenu.** Toutes les phrases vraies, y compris « le premier n'est déclenché par rien, nulle part » et l'exclusion définitive des deux filets. Une phrase **non vérifiée ici** et renvoyée à la famille 3 : « Une vente dont l'enregistrement n'a pas été copié n'est notée que s'il subsiste une transcription » (hors périmètre confié) |
119| **21** | « Rien ne relie les tables entre elles » — `preuves-travail-2026-09-09.md:121` | non retrouvée | **(c) plateforme Apteva** : P `apps/mcp/tables/tables.go:186-212` — le `CREATE TABLE` produit n'émet aucune clause `FOREIGN KEY`, `UNIQUE` ou `CHECK`, et l'appelant n'a aucun moyen d'en demander une. **(a) code déployé** : les **13** fichiers portant `tables_alter`/`tables_create` ; D1 `flexylead-update-sale.mjs:157-175` · D1 `lead-qualification-update.mjs:477-490` · D1 `flexylead-record-work-result.mjs:273-281` · D1 `flexylead-manage-facturation.mjs:68-84` · D1 `flexylead-claim-ai-reservation.mjs:25-36,190-305`. **(f) recomptage** : 13 en service, 15 dans les sources. **(d) capture** pour la colonne de l'incident d'août : `analyse/notes-visuelles.md:773` | **borné sur une phrase.** Le chiffre « treize fonctions » est **exactement celui du déployé et doit être conservé tel quel** (`:227`). « L'une d'elles crée elle-même une colonne dont l'absence avait provoqué un incident du mois d'août » **FAUX** → substitution : la colonne `effective_call_min_talk_seconds` n'est créée par **aucune** des treize ; elle l'est par un script de maintenance qui refuse de tourner ailleurs qu'en local. Le rapport signale que c'est « le genre de détail qui fait tomber un constat juste » (`:226`). Le reste tenu |
120| **22** | « La supervision est tracée d'un côté et pas de l'autre » — `preuves-travail-2026-09-09.md:133` | **Mineure** — seule gravité nommée dans les cinq rapports (`preuves-travail-2026-09-09.md:142`), et **contestée par l'agent** : « Le point le plus lourd de cette entrée — un second chemin de lecture du portefeuille d'un autre commercial, non tracé — n'est pas mineur au regard de la traçabilité. Jugement, pas fait. » → « à arbitrer ». Correspond à l'unique `sev-n` du bandeau | **(b) source non déployée** : S `commercial-supervision-session.mjs:33-95,8` · S `supervision-read-only.test.mjs:11-19` · S `flexylead-app-commercial-resource-query.mjs:53`. **(a) code déployé** : D1 `flexylead-app-commercial-bootstrap-query.mjs:1723-1724` · D1 `flexylead-claim-ai-reservation.mjs:1-42,31-33` · D1 `flexylead-claim-missed-call.mjs:584-593,620-624,2` · D1 `flexylead-unassign-lead.mjs:8,55-74` · D1 `flexylead-route-work-items.mjs:573,595-597,874-877` · D1 `flexylead-start-callback.mjs:1`. **(f) grep d'absence** : aucune écriture d'événement ni de session dans la seconde porte | **tenu, une substitution de nommage.** Les quatre phrases vraies : jeton de quinze minutes d'un côté, seconde porte sans jeton ni durée ni trace de l'autre ; prendre une opportunité non journalisé, la rendre oui ; rendez-vous IA détenu sans limite alors qu'un appel manqué repart au bout de dix minutes. Substitution : nommer `resolveCommercial` comme la seconde porte. **La gravité est le seul élément contesté** |
121| **23** | « Une vente annulée garde son verrou de trois mois, et personne ne peut l'enlever » — `preuves-travail-2026-09-09.md:146` | non retrouvée | **(a) code déployé** : D1 `flexylead-create-sales.mjs:597-602,438-440,440` · D1 `flexylead-update-sale.mjs` (grep `centre_lock` → **0**) · D1 `lead-qualification-update.mjs:909-932,910-911,929,774-808,806` · D1 `flexylead-reconcile-telephony-handoffs.mjs:622,647,652` · D1 `flexylead-start-callback.mjs:1182` · D1 `ringover-call-events.go:1293,1423,1591` · D1 `flexylead-admin-data-mutation.mjs:9` · D1 `flexylead-unassign-lead.mjs:40-53` · D1 `flexylead-ringover-smart-routing.mjs:2169`. **(f) recomptage** : 11 écritures de `centre_lock_source`, une seule remise à zéro | **borné sur trois phrases.** « Trois mois » → « **quatre-vingt-dix jours** à compter de la date de vente » (et depuis la date de vente la plus ancienne du lot, pas de l'enregistrement). « Le programme qui libère les verrous ne connaît que deux origines » → « **deux des cinq** origines de verrou, et pas celle de la vente » (`sale` et `work_assignment` ne sont jamais libérés). « Aucune fonction du logiciel ne peut rendre ce client aux autres centres » vrai **avec une borne** : le verrou n'est pas éternel, il **expire seul** → « Aucune fonction du logiciel ne peut l'enlever : il faut attendre les quatre-vingt-dix jours ». « Si elle est annulée, le verrou reste » tenu |
122
123---
124
125# Les 7 retraits — section « Ce que nous avons retiré »
126
127Source unique : `preuves-fond-2026-09-09.md:132-145`. Ce sont les sept affirmations que le document avait publiées **puis rétractées**, chacune avec le fait qui l'a fait tomber. Le verdict de la colonne « statut » porte ici sur la **justesse du retrait**, pas sur l'affirmation retirée.
128
129| n° | affirmation retirée, au mot | ce qui l'a fait tomber | statut du retrait au 09/09 |
130|---|---|---|---|
131| **R1** | « Onze fonctions manquent à la livraison. » — *Elles sont arrivées.* | `git diff --name-only 888cf02 730a5e79 -- backend` → 11 fichiers : **dix `.mjs`** (`commercial-supervision-session`, `flexylead-api-gateway-bootstrap`, `flexylead-claim-ai-reservation`, `flexylead-manage-call-routing`, `flexylead-manage-offer-catalog`, `flexylead-manage-stats-periods`, `flexylead-manage-work-routing`, `flexylead-telephony-control`, `flexylead-unassign-lead`, `flexylead-update-sale`) **plus `SNAPSHOT-ADDITIONS-20260908.json`** | **VRAI, base à préciser** → « Dix instantanés de fonctions manquaient ; ils sont arrivés le 8 septembre, avec un onzième fichier qui est leur relevé » (`:136`) |
132| **R2** | « Il n'existe aucun test automatique côté serveur. » — *Quatre-vingt-cinq fichiers de test sont arrivés.* | recomptage : **86** (83 `.test.mjs` + 3 `_test.go`), tous versés par le commit du 08/09 | **FAUX sur le nombre, VRAI sur le fond** → « Quatre-vingt-six fichiers de test sont arrivés ». **Et le corollaire « aucune commande du projet ne les lance » est VRAI** : `package.json` → `"test": "bun test src"` — `src` seulement, rien ne cible `functions/`, rien ne lance `go test` (`:137-138`) |
133| **R3** | « La prime d'un commercial se modifie sans laisser de trace. » — *Faux.* | `backend/flexylead-update-sale.mjs:1` — `AUDIT_TABLE = "commercial_sale_edit_events"` ; colonnes `:177` (`before_state`, `after_state`, `previous_version`, `actor_auth_user_id`…) ; écriture avant l'opération `:100`, confirmation après relecture `:118-123` | **retrait justifié** (`:139`). Deux précisions portées : le périmètre de « le seul endroit de la chaîne d'argent où la valeur précédente est conservée » doit être **déclaré** (3 fichiers portent `before_state`, deux ne sont pas des fonctions d'argent) ; et « celui qui modifie n'est pas un administrateur, c'est le commercial propriétaire » est **VRAI sur ce chemin** — mais `preuves-argent:87` signale qu'il existe **deux** chemins et que celui de l'administrateur laisse le moins de trace |
134| **R4** | « La porte non authentifiée peut détourner un appel. » — *Faux. Elle écrit bien un état de présence, mais l'aiguillage ne le lit pas : il décide sur un autre champ, protégé.* | `backend/ringover-call-events.go:3127` (`updateCommercialPresenceFromCallEvent`) écrit **`presence_status`**, seul fichier à le faire ; l'aiguilleur ne lit que **`availability_status`** (`backend/flexylead-ringover-smart-routing.mjs:1817,1922,2329`, zéro occurrence de `presence_status`) | **VRAI — et les deux champs sont nommables** → substitution nommant les deux colonnes et précisant qu'**aucun code ne les relie** (`:142`) |
135| **R5** | « La réserve sur l'exclusivité client […] Le réglage a été retrouvé : il ne commande pas l'exclusivité. » | `sticky_repeat` ; `backend/flexylead-ringover-smart-routing.mjs:311-327`, structure **identique** à la source ; l'exclusivité est appliquée ailleurs, dans le choix des règles comme dans la voie de secours | **VRAI, et étendu au déployé** : la démonstration du 08/09 portait sur la source, elle a été refaite sur `backend/` → la mention « à confirmer sur la version déployée » peut être retirée (`:143`) |
136| **R6** | « Sans les sources du serveur, aucun correctif n'est exécutable. » — *Les sources sont là.* | `functions/` = 156 fichiers ; les 68 déclarations du manifeste pointent toutes vers un fichier présent (vérifié par reconstruction, aucune erreur d'ouverture) | **VRAI** (`:144`) |
137| **R7** | « Notre explication du classement d'équipe qui affiche deux fois la même personne. […] trois mécanismes peuvent la produire. » | les trois clés : `backend/flexylead-app-pilotage-summary-query.mjs:337` (regroupement, repli sur le **nom**), `:364` (rapprochement avec le roster), `:655` (clé du roster) ; plus `:1794` (`commercialId: null`) et `:2060` (`"unattributed"`) | **VRAI** ; mêmes numéros de ligne dans la source et le déployé (`:145`) |
138
139---
140
141# Contrôle de cohérence
142
143## Le compte des constats : **il tombe juste**
144
145**23 constats reconstitués, 23 attendus.** 5 + 4 + 4 + 10 = 23, conforme au `grep -c '<div class="f">'` → 23 de `preuves-fond-2026-09-09.md:30`. La numérotation globale est validée par trois points de contrôle indépendants (constats 11, 12 et 17 de `relecture-coherence`), tous concordants. **Aucun numéro ne manque.**
146
147## Le compte des gravités : **il ne tombe pas juste — et ce n'est pas comblable**
148
149Le bandeau porte **10 bloquants · 12 moyens · 1 mineur** (`preuves-fond-2026-09-09.md:30` : `sev-b` → 10, `sev-m` → 12, `sev-n` → 1). **Une seule gravité sur 23 a été retrouvée nommée dans les cinq rapports :**
150
151| Gravité | Attendu au bandeau | Attribuée ici | Manquant |
152|---|---|---|---|
153| bloquant | 10 | **0** | 10 |
154| moyen | 12 | **0** | 12 |
155| mineur | 1 | **1** (constat 22) | 0 |
156| **total** | **23** | **1** | **22** |
157
158- **Constat 22** « La supervision est tracée d'un côté et pas de l'autre » = **mineur**. Double confirmation : la gravité est nommée explicitement (`preuves-travail-2026-09-09.md:142`, « Gravité "Mineure" »), et le bandeau ne compte **qu'un seul** `sev-n`. Les deux faits se recoupent sans ambiguïté.
159- **Les 22 autres sont « non retrouvée ».** Aucun des cinq rapports `preuves-*` ne relève les gravités : ils vérifient les phrases, pas les pastilles. Un balayage de gravité sur l'ensemble de `analyse/*.md` ne ramène que l'agrégat du bandeau, jamais une attribution constat par constat.
160- **Rien n'a été deviné, et une piste a été écartée délibérément.** Les fichiers du 07/09 (`audit-de-nos-releves:341,368` ; `ligne-par-ligne-lot5.md:132`) attribuent bien des gravités — « trois portent `Gravité : Bloquante` », « Gravité Mineure » pour le contrôle de cohérence — mais ils portent sur le **document antérieur à 11 cartes**, pas sur `audit-v3.html`. Les reporter serait un mélange de corpus. Elles ne figurent donc pas ci-dessus.
161
162**Conséquence : la répartition 10/12/1 n'est pas reconstituable depuis le corpus disponible.** Elle exigerait le HTML lui-même — la classe `sev-b` / `sev-m` / `sev-n` portée par chacun des 23 blocs `<div class="f">`.
163
164## Les titres : 19 sur 23 au mot, 4 approchés
165
166| | constats |
167|---|---|
168| **Titre verbatim confirmé** (ligne marquée « (titre) » ou intitulé de section) | 1, 2, 4, 6, 7, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 — **19** |
169| **Titre non retrouvé**, première phrase du constat citée à sa place et signalée comme telle | **3, 5, 8, 9** — **4** |
170
171Pour ces quatre, les rapports vérifient le corps sans citer de ligne de titre : les blocs commencent directement à la sous-phrase `.1`, là où les constats 1, 2 et 4 portent une ligne `.0` ou une mention « (titre) » explicite. La phrase donnée est donc une **phrase de corps**, pas un titre — à confirmer sur le HTML si le libellé exact importe.
172
173## Statuts : ce qui a bougé
174
175| Statut | constats |
176|---|---|
177| **Tenu tel quel** (aucune substitution sur le constat) | 9, 20 — et pour l'essentiel 19 (renforcé, pas borné) |
178| **Borné** (au moins une substitution ; titre ou corps) | 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 13, 14, 15, 16, 17, 18, 21, 22, 23 |
179| **Titre lui-même substitué** | 1, 7, 11, 14 |
180| **Retiré** | **aucun des 23.** Les 7 retraits sont des affirmations d'autres sections (voir tableau ci-dessus), pas des constats numérotés |
181| **Non publiable en l'état après substitution** | **11** — resté contradictoire avec lui-même (`relecture-coherence:11`), et **12**, qui ne peut pas être publié aux côtés de 11 en l'état (`:13`) |
182
183Deux réserves de méthode signalées par les rapports eux-mêmes, et conservées sans être rehaussées :
184
185- Les cinq rapports ont produit **58 substitutions**, dont une part porte sur la carte, le chapeau et les sections hors constats — le décompte par constat ci-dessus ne prétend donc pas se sommer à 58.
186- `relecture-coherence:7` : les substitutions ont laissé **17 passages cassés, 23 contradictions internes et 7 passages incompréhensibles** pour un lecteur non technique. Le détail intégral n'a pas été écrit en fichier ; il ne vit que dans le fil de la session du 9 septembre (`:3`, `:46`). **Il n'est donc pas récupérable ici.**
187