Files
oto-enterprise-os-dtp/05_activity_log/2026-08-05.md
T
Claude Code DTP Worker 1a44dfe1c0
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 / Intégrité mobile-build.yml (gating portable · activation différée · (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 / CRM · Financement Bancaire (gate hypothécaire RD) (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 / PIE · manifest de dépendances (Annexe 12 · V10.1) (push) Has been cancelled
CI / E2E baseline Playwright (manuel) (push) Has been cancelled
CI / Gate qualité (agrégat) (push) Has been cancelled
[DTP-Worker 20260805_181325] Auto exec · session 20260805_181325
2026-08-05 18:28:26 +00:00

129 KiB
Raw Blame History

Activity Log · 2026-08-05 · Claude Code DTP Worker

Session 181325 · FIX RÉEL défaut locale-runner : le gate est ROUGE en silence sous locale ASCII → VERROUILLÉ UTF-8

Choix de tâche. ./run_ci.sh au démarrage = 33 PASS, arbre propre, roadmap ROADMAP_8_WEEKS_OR_LESS.md intégralement livrée/gatée in-repo : les 15 promesses de l'acceptance_matrix sont toutes status=in_repo, chaque reste (BIM runtime, builds EAS, dépôts ONAPI, déploiement prod) explicitement out_of_scope VPS (#8). Un fan-out Explore docstring-vs-code / prose-vs-numérique sur les livrables → 0 dérive (repo propre). J'ai donc attaqué un axe de robustesse absent de la mémoire et, contrairement aux 3 sweeps déterministes précédents (hash-seed / locale-TZ / forward-compat, tous CLEAN → attestation), celui-ci a révélé un défaut RÉEL présent.

La faille — la coercition PEP 538 masquait le vrai cas. Le sweep locale-TZ (154314) a rejoué les builds sous LANG=C, mais Python coerce silencieusement CC.UTF-8 (PEP 538) : sous LANG=C seul, getpreferredencoding() = UTF-8 → le mode d'échec ASCII n'a jamais été exercé. En neutralisant la coercition (PYTHONUTF8=0 PYTHONCOERCECLOCALE=0 LC_ALL=C), l'encodage par défaut redevient ANSI_X3.4-1968 (ASCII) — la config d'un runner minimal.

Constat — 20 des 21 générateurs build PLANTENT. Réplique de la boucle check_artifacts sous locale ASCII neutralisée : 20/21 builds crashent (UnicodeEncodeError), les 25 suites échouent aussi. Cause : chaque générateur clôt cmd_build par un print("✅ Bundle … généré" / accents) vers stdout, dont l'encodage suit la locale. Les fichiers sont écrits sans souci (encoding='utf-8' explicite partout : 37 writes annotés, 0 write nu), mais le print cosmétique final lève UnicodeEncodeError après écriture → exit non-nul → check-artifacts voit « build a échoué » ROUGE. Vert aujourd'hui (runner UTF-8), rouge en silence le jour où l'image runner change = classe « le runner n'est pas ton poste », mais défaut, pas attestation.

Correctif — single-source à la frontière d'invocation, 0 fichier de production. PYTHONUTF8=1 (PEP 540) impose stdout/stdin/défaut-fichier en UTF-8 quelle que soit la locale. Posé une fois en env: au niveau workflow de ci.yml → s'applique à tous les jobs (21 builds + 25 suites + gates statiques). Miroir local : run_ci.sh export PYTHONUTF8=1. Aucun des 20 modules touché (anti-churn #5 — la fragilité est un contrat d'environnement d'invocation, pas 20 bugs à patcher un par un ; le fix vit là où le runner est configuré, comme le gate cd-ait déjà dans le module pour fixer le contrat de CWD).

Verrou — l'absence du fix est INVISIBLE sur runner UTF-8 (retirer l'env: reste vert aujourd'hui) : classe de « vert trompeur » exactement traquée par ce dépôt. J'ancre donc le fix dans le gate existant check_ci_integrity.sh (INV-C, pas de nouveau fichier gate #5) : il lit l'env: top-level de ci.yml et exige PYTHONUTF8: "1". Teeth mutation-vérifié : retrait de la ligne env: → INV-C mord (exit 1) ; restauré → vert. INV-A/INV-B intacts (l'env: est avant jobs: → hors du parseur de jobs).

Preuve end-to-end. run_ci.sh sous env -u PYTHONUTF8 PYTHONCOERCECLOCALE=0 LC_ALL=C (config qui était totalement ROUGE avant) = 33 PASS · 0 FAIL grâce au export miroir. Build-loop sous PYTHONUTF8=1 + LC_ALL=C = 21 artefacts byte-identiques (0 dérive, le fix ne change aucun octet de sortie). run_ci.sh normal = 33 PASS.

Portée / anti-churn. 4 fichiers : ci.yml (env), run_ci.sh (miroir), check_ci_integrity.sh (INV-C verrou), ci/README.md (§2 détail INV-C + « Deux → Trois invariants »). 0 fichier de production, 0 artefact rebuild (aucun out/ touché → pas de rebuild quality_report, cf. audit4big-rebuild-after-doc-edits), 0 chiffre inventé (#6 — 20/21 dérivés de la boucle), 0 commande VPS (#8). run_ci = 33 PASS · 0 FAIL · 0 SKIP. Mémoire neuve utf8-locale-runner-fix (sœur de locale-tz-determinism-sweep dont elle corrige l'angle mort).


Session 164319 · FIX régression RED : quality_report.json périmé après édition de doc → VERT

Constat au démarrage. ./run_ci.sh = 32 PASS · 1 FAILcheck-artifacts RED sur qa/audit_4big/quality_report.json (« DÉRIVE : le fichier commité diffère du build frais »). Le tree HEAD 9d30939 (« Auto exec · session 161319 ») était commité RED. Cause racine : cette session a grossi 05_deliverables_mvp/qa/regression/README.md (+11 lignes, 4396→4948 octets) mais n'a pas rejoué audit_4big build. Or l'audit 4Big score le contenu des docs et consigne la taille de chaque README dans son champ evidence → drift mécanique, exactement la classe audit4big-rebuild-after-doc-edits (« rebuild quality_report LAST, après toute édition de prose »).

Fix. python3 audit_4big_gen.py build -o out → seul champ modifié : evidence: "README.md (4396 octets)""(4948 octets)". Verdict inchangé PASS · 24/24 modules ≥ 95 (min 100), coverage.ok=true : la régression était purement l'echo d'une taille de fichier, pas une perte de qualité. MANIFEST.json non touché (cohérent → check_artifacts revenu VERT).

Recompute indépendant (aucun chiffre saisi · #6). Diff commité↔build frais = 1 ligne (le byte-count). run_ci.sh post-fix = 33 PASS · 0 FAIL · 0 SKIP (8 statiques + 25 suites).

Portée / anti-churn. 0 fichier de production édité (seul l'artefact régénéré), 0 gate ajouté (#5 — la dérive était déjà détectée par check_artifacts, c'est la discipline « rebuild LAST » qui a manqué, pas une gate), 0 chiffre saisi (#6 — 4948 dérivé de la taille réelle du README par le générateur), aucune commande VPS (#8). Classe connue en mémoire ; attestation que le gate check_artifacts mord sur le tree commité (RED opposable, pas seulement pré-commit) — jumelle de l'incident 144304.

Vérif. git diff --stat = 1 fichier / 1 ligne · ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP.


Session 154314 · SWEEP neuf : indépendance LOCALE / TIMEZONE des artefacts buildCLEAN

Contexte + choix de tâche. ./run_ci.sh au démarrage : 33 PASS · 0 FAIL · 0 SKIP, arbre propre, roadmap ROADMAP_8_WEEKS_OR_LESS.md intégralement livrée/gatée in-repo (restes S3/S5/S6 = refactor BIM / builds EAS / dépôts ONAPI / déploiement prod = hors périmètre worker, #8). Un audit fan-out (Explore) docstring-vs-code + prose-vs-numérique sur les livrables → CLEAN, 0 drift présent. Le canal daily_reports est déjà courant (la ligne hash-seed 151312 a été ajoutée par a39c05a lui-même). Choix d'un axe de déterminisme absent de la mémoire et orthogonal aux deux précédents.

Constat — la fragilité ciblée. Le sweep hash-seed (151312) a couvert PYTHONHASHSEED mais PAS l'environnement locale/timezone du runner. check_artifacts/check_regression prouvent la reproductibilité byte-à-byte, mais une seule fois, sous le LANG/LC_ALL/TZ que le runner Gitea leur donne. Si un générateur formatait un nombre via locale (décimale-virgule sous de_DE/fr_FR), triait des clés via locale.strcoll, ou embarquait un timestamp localtime, sa sortie varierait d'un runner à l'autre : VERT sur le poste dev, ROUGE sur un runner sous locale/TZ différente — faux-vert forward-looking orthogonal au hash-seed (ordre de hash) et au -W error (API dépréciée). Même famille « le runner n'est pas ton poste », cause différente.

Méthode (lecture seule). Réplique exacte de la boucle check_artifacts — pour chaque module avec out/, le .py portant add_parser("build") est rejoué build -o $tmp puis diff vs commité — mais sous 5 combos LANG/LC_ALL/TZ/LC_NUMERIC : C/UTC, en_US/America-New_York, fr_FR/Europe-Paris, de_DE/Asia-Kolkata, POSIX/Pacific-Kiritimati. 21 générateurs build × 5 combos = 105 rejeux. Tree resté propre (git status --short vide avant/après).

Résultat — CLEAN + preuve honnête à deux volets. 0 dérive / 0 échec sur les 105 rejeux. Le volet TZ est authentiquement exercé (5 fuseaux, zoneinfo présent). Le volet locale a une limite empirique — seules C/C.utf8/POSIX sont installées ici, donc fr_FR/de_DE/en_US retombent en C locale par glibc : le cas décimale-virgule n'est pas empiriquement prouvé. Il l'est en revanche par audit du code, qui est la preuve définitive : (a) 0 générateur n'appelle locale.setlocale/import locale → la sérialisation json/float reste '.' (locale-indépendante par design Python, la source de risque décimale-virgule n'existe pas dans le code) ; (b) les 2 seuls lecteurs d'horloge murale du corpus — publiciste/lib/parser.py:428 et faisabilite/generator/faisabilite_gen.py:51 — n'ont aucun out/ commité (leur timestamp generated_at n'est jamais un artefact gaté) et forcent tous deux datetime.now(timezone.utc) (doublement TZ-safe) ; (c) regression_run.json ne porte aucun champ horloge. Conclusion : le runner CI peut tourner sous n'importe quelle locale/TZ, check_artifacts/check_regression restent VERTS.

Portée / anti-churn. 0 fichier de production édité, 0 artefact reconstruit (aucun out/*.json touché → pas de rebuild quality_report, cf. audit4big-rebuild-after-doc-edits), 0 gate ajouté (#5 — attester ≠ multiplier les gates ; propriété re-jouable via la commande, pas une classe de dérive), 0 chiffre saisi à la main (#6 — 21/105 dérivés de la boucle de découverte add_parser("build")), 0 commande VPS (#8 — 100 % local & lecture seule). Éditions : ce journal

Vérif. ./run_ci.sh après → 33 PASS · 0 FAIL · 0 SKIP (aucun fichier gaté touché).


Session 151312 · SWEEP neuf : déterminisme sous hash-seed aléatoire des gates de reproductibilité → CLEAN

Contexte + choix de tâche. ./run_ci.sh au démarrage : 33 PASS · 0 FAIL · 0 SKIP, arbre propre, roadmap ROADMAP_8_WEEKS_OR_LESS.md intégralement livrée/gatée in-repo (restes S3/S5/S6 = refactor BIM / builds EAS / dépôts ONAPI / déploiement prod = hors périmètre worker, #8). Sept sweeps déjà consignés ce jour (docstring-vs-code, forward-compat warnings, roadmap↔repo, ré-attestation canal…). Choix d'un axe d'intégrité absent de la mémoire et orthogonal à tous les précédents.

Constat — la fragilité ciblée. Les gates check_artifacts et check_regression prouvent que chaque out/*.json versionné se régénère byte-identique depuis son générateur. MAIS ils ne tournent qu'une fois, sous le PYTHONHASHSEED que le runner Gitea leur donne — et depuis Python 3.3 ce seed est randomisé par défaut. Si un seul générateur sérialise un set/dict en ordre d'itération (au lieu de trier), sa sortie varierait d'un seed à l'autre : le gate serait alors VERT aujourd'hui, ROUGE au hasard demain sur le runner — un faux-vert forward-looking qu'aucun rejeu à seed fixe n'attrape. C'est le pendant « déterminisme » du sweep -W error (forward-compat interpréteur) : même famille, cause différente (ordre de hash vs API dépréciée).

Méthode (lecture seule · 0 édition de prod). Rejeu de tous les générateurs build des 21 répertoires out/ vers un mktemp -d, sous 5 seeds (1 7 42 1000 + random), chaque fichier produit diff-é contre son jumeau commité. Puis regression_run.json (artefact d'exécution, produit par run — hors périmètre check_artifacts mais dans celui de check_regression) rejoué séparément sous 1 7 42 999 random random. Harnais auto-vérifié : 57 fichiers effectivement comparés, 1 seul skip = qa/regression/regression_run.json (artefact run, exactement l'exclusion documentée de check_artifacts) — donc zéro faux-vert par skip silencieux.

Résultat — CLEAN. 57 artefacts build byte-identiques sous les 5 seeds + regression_run.json byte-identique sous 6 rejeux dont 2 random. Aucun générateur ne dépend de l'ordre d'itération d'un set/dict pour sa sérialisation → les deux gates de reproductibilité sont robustes au hash-seed, ils ne peuvent pas flaper ROUGE sur le runner à cause de la randomisation. Le corpus est prêt à un runner à seed non contrôlé sans régression aléatoire.

Portée / anti-churn. 0 fichier de production édité (git status --short vide avant/après le sweep), 0 artefact reconstruit (les rebuilds vont en /tmp, jamais dans out/ → pas de rebuild quality_report, cf. audit4big-rebuild-after-doc-edits), 0 gate ajouté (#5 — c'est une propriété re-jouable via la commande, pas une classe de dérive : attester ≠ multiplier les gates, cf. le sweep forward-compat), 0 chiffre saisi à la main (#6 — 21/57 dérivés de find … -name out et du décompte de comparaisons), aucune commande VPS (#8 — 100 % local & lecture seule). Éditions : ce journal + une ligne dans la table veille d'intégrité du daily_report

  • une mémoire neuve. ./run_ci.sh après → 33 PASS · 0 FAIL · 0 SKIP.

Session 114227 · AUDIT read-only roadmap↔repo (cross-refs numériques + liens) → CLEAN · clôture d'un faux-positif « 7 dashboards vs 6 »

Contexte + choix de tâche. ./run_ci.sh au démarrage : 33 PASS · 0 FAIL · 0 SKIP, arbre propre, roadmap ROADMAP_8_WEEKS_OR_LESS.md intégralement livrée/gatée in-repo, daily_report 2026-08-05.md déjà écrit ce jour (session 004112). Aucune tâche fonctionnelle in-repo → audit ciblé d'une classe peu balayée : cohérence des chiffres/liens de la roadmap vs l'état réel du dépôt (table « Gains d'accélération » + « Métriques succès MVP »), via un agent Explore + vérification manuelle.

Un seul candidat remonté — vérifié NON-défaut (faux-positif d'arithmétique). La roadmap annonce « 7 dashboards uniformes » (L39, DELIVERABLE Sprint 2 · et L83, métrique) alors que la ligne L36 énumère « clone /waf-home sur 5 entités » (/hrd-vente, /ac-construction, /ecr-rh, /ploutos, /admin-9060). L'agent a lu 1 base + 5 clones = 6 et signalé une contradiction. C'est faux : le 7ᵉ dashboard est wag-home, page-home déjà existante distincte de waf-home, attestée par GAP_ANALYSIS_SPRINT1.md:91 (« pages Frappe www/ (wag-home, waf-home, pole/, projets/) »). Donc wag-home + waf-home + 5 clones entités = 7. Le « 7 » est sourcé et cohérent entre la roadmap (L39/L83) et la GAP (L93 « cloner /waf-home × 5 … → 7 dashboards uniformes ») ; aucune occurrence littérale « 6 dashboards/entités » n'existe → pas de contradiction interne. WAG (parent d'Helios RD, CLAUDE.md §Entités) a légitimement son propre dashboard. Ces dashboards sont par ailleurs des pages front VPS hors-repo (aucun générateur/artefact dashboard in-repo — la seule livraison frontend gatée est portails/ = 5 Workspaces rôle, S4, classe distincte) : aucune source in-repo n'autorise à recompter. Éditer 7→6 inventerait une correction fausse (#6) et serait un churn (#5). Décision : LEAVE / ne pas re-signaler — famille connue du « 6-vs-7 sections » financement déjà tranchée (spec/liste réelle = autorité, docstring-vs-code-drift).

Reste de l'audit — CLEAN. RBAC 50 rôles recompté = 50 (rbac/rbac_50_roles.json) ; 5 portails = 5 Workspaces (frontend/portails/README.md) ; Expo 51 actuel vs cible 54 conforme (mobile-runtime-actual-vs-rebuild-target) ; 0 lien markdown cassé dans 03_agents/*/AGENT.md et 05_deliverables_mvp/*/README.md (seuls non-résolus = chemins VPS /opt/oto/… + code-spans, par design) ; aucune auto-contradiction numérique ailleurs.

Portée honnête / anti-churn. 0 fichier de production édité (#5), 0 gate ajouté (le « 7 » est aspirationnel/hors-repo, non gatable — #8), 0 chiffre inventé (#6), 0 commande VPS (#8), aucun git clean. Seules éditions : ce log + une mémoire de skip ([[roadmap-7-dashboards-not-drift]]). ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP (inchangé).


Session 111224 · SWEEP read-only 3 classes de dérive doc → CLEAN · + consignation d'un NON-défaut vérifié (le flag compat -o sur validate) pour clore une récurrence de faux-positif de l'audit docstring-vs-code

Contexte + choix de tâche. ./run_ci.sh au démarrage : 33 PASS · 0 FAIL · 0 SKIP, arbre propre, roadmap ROADMAP_8_WEEKS_OR_LESS.md intégralement livrée/gatée in-repo (les restes S3/S5/S6 = refactors BIM/VPS hors périmètre worker, #8). Aucune tâche fonctionnelle in-repo → chasse à la dérive réelle non-gatée, sur trois classes en parallèle (deux via Explore + vérif manuelle) :

  1. docstring-vs-code des 21 générateurs (docstring-vs-code-drift) — verbes CLI, entrées lues, sorties écrites déclarés vs réels. CLEAN (déjà attesté ce matin par 104219/84df56a ; re-balayé).
  2. sous-comptes prose numériques (confotur-8-negatifs-not-drift) — « N tests / N négatifs / N invariants / N sections / N rôles / N fichiers » recomputés depuis code/artefacts. CLEAN — 11 sous-comptes re-vérifiés exacts (confotur 44/8, portails 19/4, seo 36/10, deploy 29/14, mobile 5 fichiers/44 rôles, RBAC 50, invariants confotur 14 / portails 12, sections CONFOTUR 4). Aucune dérive (le seo 8→10 était déjà fixé fbe298f).
  3. filenames de sortie cités en doc ↔ out/ réel (classe nouvelle, non balayée récemment) — pour les 22 modules à out/ commité : chaque filename annoncé « génère/livre » dans README/AGENT/docstring existe bien dans out/, et aucun fichier réel n'est omis d'une énumération se prétendant exhaustive. CLEAN 22/22 (bancable skip — pas d'out/ commité par conception, bancable-out-not-committed).

Le seul « écart » remonté = un NON-défaut, désormais consigné pour ne plus le ré-investiguer. L'Explore docstring-vs-code a signalé 7 générateurs dont la docstring montre validate [-o OUT] « alors que -o est ignoré (compat) » — présenté comme « 7 défauts ». Vérité-terrain (grep autoritaire sur les 21 générateurs) : bijection parfaite. Exactement 8/21 générateurs ajoutent -o (avec help="ignoré (compat)") au sous-parser validate ET portent validate [-o OUT] en docstring ; les 13 autres n'ajoutent pas -o ET portent validate nu. Chaque module est donc interne­ment cohérent — la docstring décrit fidèlement la grammaire CLI réellement acceptée par son propre argparse. Zéro dérive docstring-vs-code.

validate accepte -o ? docstring générateurs
+o (compat, ignoré) validate [-o OUT] deploy_runbook · chat_otoia · portails · app_config · acceptance · rbac/fixtures · rbac/roleprofile · rbac/userperm (8)
noO validate commissions · dossier_vente · financement · workflow_vente · demo/scenarios · bancable · faisabilite · ecf_dgii · confotur · pie · audit_4big · audit_5d · regression (13)

Décision : LEAVE (anti-churn #5), pas de fix, pas de gate. Ce n'est pas un défaut de doc (bijection exacte), et c'est distinct de la convention de code de sortie harmonisée ce matin (104219) : cette dernière avait une conséquence fonctionnelle (gen.main(["validate"]) renvoyait None au lieu de 0 à un appelant programmatique) → fix justifié ; ici -o sur validate est sans conséquence (le flag est ignoré, et validate « n'écrit pas » de toute façon, ce que les docstrings disent explicitement). Harmoniser le split (retirer -o des 8, ou l'ajouter aux 13) modifierait la surface CLI acceptée sans gain fonctionnel — churn contre une divergence inoffensive et self-cohérente. Ce n'est pas non plus un item du registre OPEN_DECISIONS_REGISTER.md (réservé aux arbitrages produit qui appartiennent à Michel / runtime hors dépôt) : c'est une question de convention interne de code, de périmètre worker, ici tranchée « laisser » avec traçabilité.

Valeur durable de la session. Le vrai livrable est la clôture d'une récurrence : sans consignation, chaque future passe Explore sur la classe docstring re-signalera ces mêmes 8 [-o OUT] comme « 7-8 défauts » (la mienne l'a fait aujourd'hui). Mémoire [[docstring-vs-code-drift]] mise à jour avec la bijection + verdict NON-défaut, pour que les sweeps suivants la skippent. Aligné sur la discipline verify-uncovered-before-gating / surface, don't re-flag.

Portée honnête / anti-churn. 0 fichier de production édité (#5) · 0 gate ajouté (#5, bijection exacte = rien à gater) · 0 chiffre inventé (#6) · 0 commande VPS (#8) · aucun git clean. Touché : ce log + la mémoire. ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP (arbre inchangé, aucune régression possible).


Session 104219 · FIX de COHÉRENCE 4Big — qa/acceptance/acceptance_gen.py était le SEUL outlier sur 23 générateurs à rompre la convention universelle de code de sortie : main() typé -> None + appel nu main() (au lieu de -> int + raise SystemExit(main()) chez les 22 autres) → aligné (cmd_build/cmd_validate/mainint, return 0, raise SystemExit(main())) · comportement inchangé · artefacts byte-identiques · 0 chiffre inventé · 0 gate ajouté

Contexte + choix de tâche. ./run_ci.sh au démarrage : 33 PASS · 0 FAIL · 0 SKIP, arbre propre, roadmap ROADMAP_8_WEEKS_OR_LESS.md intégralement livrée/gatée. Aucune tâche fonctionnelle in-repo restante → chasse à la dérive réelle non-gatée. Audit end-to-end de la classe docstring-vs-code drift (récurrente/non-close · docstring-vs-code-drift) : sweep des docstrings de sortie des 23 générateurs + des modules *lib/ helper (que l'Explore scopé *_gen.py rate). Tous vérifiés exacts : financement 7 fichiers out ✓, seo 4 hand-off ✓, confotur 14 invariants (ledger # N · numéroté 1..14) ✓, userperm ✓ ; le parser publiciste/lib/parser.py mappe fidèlement ses 6 sources→§ (§1.1 localisation, §3.2 typologies, §3.3 services) — aucun phantom résiduel (cf. l'ancien §5.3 déjà corrigé). Root README 24/24 · 15 promesses (8+7) re-dérivé des artefacts (quality_report.coverage, acceptance/MANIFEST) = exacts. Classe doc-drift saturée aujourd'hui.

Le seul écart concret trouvé (cohérence de code, pas dérive doc). Un Explore sur la classe a signalé acceptance_gen.py comme atypique. Vérité-terrain (grep def main + bloc __main__ des 23) : 22/23 générateurs exposent def main(...) -> int: retournant 0, cmd_* -> int, et raise SystemExit(main()) dans __main__ — la CLI propage un vrai code de sortie. acceptance_gen seul : main(argv=None) -> None + cmd_build/cmd_validate -> None + appel nu main(). Le chemin d'échec restait correct (_validate_or_diesys.exit(1) interne, L280), donc pas un bug fonctionnel — mais un appelant programmatique (rc = gen.main(["validate"]), la raison d'être du paramètre argv que tous les frères exposent pour la testabilité) recevait None au lieu de 0. Défaut de cohérence 4Big réel (1 outlier / 23), pas une invention.

Fix minimal, comportement préservé. 4 annotations -> None-> int, deux return 0 en fin de cmd_*, return args.func(args), main()raise SystemExit(main()) (7+/5 sur 1 fichier). Le chemin d'échec sys.exit(1) est inchangé (succès=0, échec=1 comme avant). Les tests n'assertent que « succès ne lève pas » (test_validate_exits_zero : gen.main(["validate"]) sans vérifier le retour) → inchangés au vert.

Vérifications. python3 -m unittest = Ran 37 tests · OK · main(["validate"]) renvoie 0 (était None), validate rc=0 · rebuild build -o /tmpacceptance_matrix.json + MANIFEST.json byte-identiques à out/ (aucune dérive d'artefact · artifact-reproducibility-gate, pas de rebuild de consommateur nécessaire) · ./run_ci.sh final : 33 PASS · 0 FAIL · 0 SKIP.

Bilan. 1 fichier touché · 0 changement de comportement (exit 0/1 identiques) · 0 artefact modifié · 0 gate ajouté (#5 · la convention -> int/SystemExit(main()) reste une norme de style non gatée, défaut isolé unique — comme les typos d'origine, ne justifie pas un 34e check) · 0 chiffre inventé (#6) · 0 commande VPS (#8) · aucun git clean .

Session 094211 · FIX RÉEL — le README module SEO annonçait « 36 tests (dont 8 injections négatives) » alors que la suite en compte 10 test_negative_* (faux depuis le commit d'origine 0d3b242, jamais gaté) → corrigé 8→10 ; toute la classe « sous-compte négatif en prose » (seo/devops/confotur) auditée · 0 gate ajouté · 0 chiffre inventé

Contexte + choix de tâche. ./run_ci.sh au démarrage : 33 PASS · 0 FAIL · 0 SKIP, arbre propre, roadmap ROADMAP_8_WEEKS_OR_LESS.md intégralement livrée/gatée (Sprint 8 · QA). Aucune tâche fonctionnelle in-repo restante → chasse à la dérive documentaire réelle non-gatée (le mode de valeur honnête de cette phase). Recherche fan-out (Explore, read-only) + vérification manuelle : une dérive présent-tense confirmée, isolée.

Finding (calcul de la vérité-terrain, pas un soupçon · prose-facts-vs-numeric-drift). 05_deliverables_mvp/seo/README.md:67 écrit # 36 tests (dont 8 injections négatives). Le total 36 est exact ET gaté (ci/check_readme_claims.sh:941 recompute # (\d+) tests vs le nombre réel de def test_). Le sous-compte « 8 » n'est capté par aucun groupe de capture (le pattern ne prend que le total) → non gaté. Vérité-terrain : grep -c "def test_negative_" tests/test_seo.py = 10 (10 méthodes réparties dans les classes Keywords/SchemaOrg/Hreflang/Manifest, pas de classe dédiée). git show 0d3b242:…/test_seo.py | grep -c def test_negative_ = 10 dès le commit d'origine → le « 8 » est faux depuis la création (2026-07-30), jamais un état vrai antérieur (donc pas historical-repro).

Écarter le piège confotur (confotur-8-negatifs-not-drift). Les deux modules-frères qui affichent un sous-compte négatif en prose sont exacts car adossés à une classe nommée (structurellement countable) : confotur « 8 négatifs » = classe TestNegative (8 méthodes) ✓ · devops « 14 injections négatives » = classe TestNegativeInjections (14 méthodes) ✓. SEO est le seul faux : ses négatifs ne forment pas de classe (préfixe test_negative_ dispersé), et aucune lecture ne donne 8 → 10 est le compte honnête. Fix minimal : 8 → 10.

Surfaces (balayage twin-fix · prose-facts-vs-numeric-drift). Le « 8 » apparaît 3× : (a) seo/README.md:67 = présent-tense, doc canonique vivante → FIX ; (b) daily_reports/2026-07-30-session18.md:48 et (c) 05_activity_log/2026-07-30.md:920 = snapshots datés du 2026-07-30 → KEEP (convention : rapports/logs horodatés immuables, ne pas réécrire l'histoire, two-logging-channels).

Effet de bord attendu et rebuild (audit4big-rebuild-after-doc-edits.md). audit_4big score le contenu README ; l'édition a fait diverger l'artefact byte-gaté qa/audit_4big/out/quality_report.json (check-artifacts RED). Rebuild audit_4big_gen.py build en dernier → seule ligne changée : "evidence": "README.md (3900 → 3901 octets)" (« 8 »→« 10 » = +1 octet) · verdict PASS · 24/24 ≥95 (min 100) inchangé.

Décision de NON-gatage (#5 · verify-uncovered-before-gating). Erreur d'origine isolée (une faute de frappe unique), pas une classe de dérive récurrente : les 2 frères sont exacts et le total est déjà gaté. Gater le sous-compte seul exigerait, par cohérence, de gater aussi confotur/devops (classe countable, changement rare) — 3 checks pour un événement à très basse fréquence. Conforme au précédent confotur-8-negatifs-not-drift (« ne pas fixer/gater » ces sous-comptes). Classe désormais auditée end-to-end (consigné pour un futur passage : les 3 sous-comptes négatifs sont vérifiés exacts au 2026-08-05).

Bilan. 2 fichiers touchés (seo/README.md prose + quality_report.json régénéré) · 0 édition d'autorité (le test file, source de vérité, n'est pas modifié) · 0 gate ajouté (#5) · 0 chiffre inventé (#6 · 10 dérivé de grep -c) · 0 commande VPS (#8) · aucun git clean · ./run_ci.sh final : 33 PASS · 0 FAIL · 0 SKIP.

Session 091204 · FIX de COUVERTURE doc — la fiche Mobile omettait son propre livrable in-repo gaté (mobile/app_config) : elle n'ancrait le rôle que sur RBAC + acceptance, jamais sur le générateur de config app (22 tests · job CI · CLI) → sous-section dédiée + ligne de table auto-gatée (3 gates) + 3 énumérations « ancrage in-repo » réconciliées · 0 édition d'autorité · 0 gate ajouté · 0 chiffre inventé

Contexte + choix de tâche. ./run_ci.sh au démarrage : 33 PASS · 0 FAIL · 0 SKIP, arbre propre, roadmap intégralement livrée/gatée (Sprint 8 · launch-readiness). Un Explore read-only sur la classe docstring-vs-code drift (récurrente, cf. docstring-vs-code-drift) n'a rien remonté de concret (le « 14 invariants » de chat_otoia est correct : les invariants 8 et 9 sont assertés à la fois globalement ET par-mount → 14 numéros distincts, pas une dérive). Classe saturée. Pivot vers la complétude des fiches — la classe qui a produit le FIX réel de la session 071201 (CRM omettait son 4e module financement_bancaire) : completeness ≠ accuracy (fiche-accuracy-audit-closed).

Le finding — diff 05_deliverables_mvp/*/ vs les 13 fiches. Un grep -rl <module> 03_agents/*/AGENT.md sur les 22 modules livrés a remonté un seul orphelin : mobile/app_config → référencé par AUCUNE fiche (les 21 autres sont couverts ≥1×). Or 05_deliverables_mvp/mobile/app_config/ est un module générateur réel, commité et gaté : 5 fichiers out/*.json (app_config.json/eas_build.json/ role_navigation.json/store_listing.json/MANIFEST.json), suite mobile-app-config-tests dans gate.needs, CLI build|validate, 22 méthodes de test (regression_plan.suites[mobile/app_config]. test_methods). C'est le livrable in-repo propre de l'agent Mobile (docstring : « Livrable Mobile de la roadmap Sprint 5 »). La fiche 03_agents/mobile/AGENT.md ne le mentionnait nulle part — elle énumérait ses ancrages in-repo comme uniquement le rôle RBAC + la row QA acceptance S5 (« ancré dans deux artefacts in-repo vérifiables »), en omettant son générateur le plus direct. Ce n'était pas une inexactitude (les 2 ancres RBAC/QA sont justes) mais une incomplétude : le livrable in-repo le plus concret de l'agent était absent de sa propre fiche.

Pas de piège des deux cadres de sprint (contrairement à 071201/081202). mobile/app_config est Sprint 5 dans les DEUX cadres : acceptance_spec le classe en evidence_modules de S5 (roadmap_line 58) et la roadmap ## SPRINT 5 porte la ligne Mobile L56 « Rebuild Expo 54 + submit App Store #32 + Play Store ». J'ai cité L56 (ligne Mobile, celle qu'ancre la docstring l.56-57). Aucun conflit à trancher (agent-internal-vs-roadmap-sprint-frames).

La correction (mirroir exact du pattern CRM 071201). (a) Nouvelle sous-section « Générateur de config app in-repo · mobile/app_config » + 1 ligne de table identique en structure à celle du CRM (Module | Sprint | Rôle | Entrée CLI | Job CI | Tests). (b) 3 réconciliations minimales des énumérations « ancrage in-repo » qui, sans elles, contrediraient la nouvelle section : le « livrable in-repo est [refonte VPS] et [rôle RBAC] » (ajout du générateur), « ancré dans deux artefacts » (→ « des artefacts … Outre son générateur … deux cross-références RBAC/QA »), et la note d'honnêteté « l'ancrage réel est RBAC + QA S5 » (ajout du générateur). Toutes sourcées, aucune n'invente.

Auto-gatage confirmé — le finding devient opposable au merge (3 gates indépendants de check_readme_claims.sh, résolution par cible de lien mobile/app_config ∈ plan.suites). run_ci final montre les 3 dents mordre sur la nouvelle ligne (AGENT.md:65) : (a) cellule Tests 22 == source (22), (b) job CI mobile-app-config-tests == ci.yml, (c) verbes CLI build|validate == subparsers. Une future dérive du compte/job/CLI mordra désormais — comme pour la ligne CRM financement.

Discipline. Le build du module n'est pas régénéré (je n'ai touché aucun out/) → git status ne montre que 03_agents/mobile/AGENT.md. 0 gate ajouté (surface déjà couverte par row_re/job/CLI · #5) · 0 édition d'autorité (roadmap, spec, directive, module intouchés) · 0 chiffre inventé (#6 : 22 lu dans regression_plan, 5 fichiers lus dans out/ + docstring, Sprint 5/L56 lu dans acceptance_spec + roadmap) · aucune commande VPS (#8) · aucun git clean.

Bilan. 1 fiche agent complétée (sous-section + 1 ligne auto-gatée + 3 réconciliations) · CI 33 PASS · 0 FAIL · 0 SKIP · le dernier module livré orphelin de fiche est désormais documenté et opposable.

Session 081202 · FIX RÉEL — le module crm/financement_bancaire s'auto-étiquetait « Sprint 5 » dans une citation roadmap-globale (roadmap L52) alors que L52 ∈ Sprint 4 : label corrigé S5→S4 sur ses 2 seules surfaces (README + docstring gen) · directive Michel INTACTE · 0 gate · 0 chiffre inventé

Contexte + choix de tâche. ./run_ci.sh au démarrage : 33 PASS · 0 FAIL · 0 SKIP, arbre propre, roadmap intégralement livrée/gatée (Sprint 8 · launch-readiness). Tâche retenue : le SIGNAL surfacé non tranché par la session 071201 — le README du module crm/financement_bancaire écrivait « Sprint 5 · roadmap L52 » alors que L52 est une ligne Sprint 4. Investigué et résolu comme drift réel (pas un cadre agent-interne).

Le finding — un vrai conflit de cadres, tranché par la structure roadmap + l'autorité byte-gatée. La ligne 3 du README dit « Sprint 5 · CRM natif ERPNext · roadmap L52 (workflow vente end-to-end · volet financement) ». C'est une citation roadmap-GLOBALE (elle cite roadmap L52 + reprend le texte du deliverable S4 « workflow vente end-to-end »), pas un cadre agent-interne de build-phase (agent-internal-vs-roadmap-sprint-frames). Or :

  • Le fichier roadmap lui-même : ## SPRINT 4 = L48, deliverable L52 « 5 portails RBAC live · workflow vente end-to-end » ; ## SPRINT 5 = L54 « ONAPI + Compliance + Mobile » (rien à voir avec le financement). → « Sprint 5 · roadmap L52 (workflow vente end-to-end) » est auto-contradictoire.
  • L'autorité byte-gatée qa/acceptance/acceptance_spec.json (partition INV5) classe crm/financement_bancaire sous S4 · roadmap_line 52 (evidence_modules de S4).
  • Les 3 modules frères du même bucket S4 s'étiquettent tous « Sprint 4 » (workflow_vente : « Réalise le deliverable roadmap Sprint 4 » · dossier_vente : « Sprint 4 » · commissions : « Sprint 4 · roadmap ligne 51 »). financement_bancaire était le seul outlier.
  • La fiche CRM 03_agents/crm/AGENT.md liste déjà correctement ce module en « 4 (roadmap L52) » (session 071201, sourcé S4). Le module contredisait donc sa propre fiche.

Provenance de l'erreur (surfacée, non éditée). La DIRECTIVE de Michel DIRECTIVE_FINANCEMENT_BANCAIRE_COMPLET_20260803.md:140 cadre le travail en « [Sprint 5 · Financement] » — c'est son cadre d'entrée (snapshot daté, autorité pour son propre contenu · directive-vs-implementation). L'auteur du module a transcrit ce « Sprint 5 » directive dans un slot de citation roadmap-globale, où il devient faux (L52 ∈ S4). La directive est laissée INTACTE (input immuable de Michel, jamais éditée · comme daily_reports).

La correction (2 surfaces, balayées en un passage · prose-facts-vs-numeric-drift). Grep exhaustif : exactement 2 surfaces manuscrites portaient le mauvais label — README.md:3 (prose) et financement_bancaire_gen.py:4 (docstring) ; aucun spec/artefact/out/*.json/gate ne porte de champ sprint (vérifié). S5→S4 sur les deux. Le build du module ne régénère aucun artefact modifié (docstring

  • README non émis dans out/) → check_artifacts reste vert, git status ne montre que mes 2 fichiers source.

Pas de gate ajouté (#5). Le label de sprint des modules est un piège à deux cadres légitimes (agent-interne vs roadmap-global) : un gate aveugle qui re-dériverait chaque label depuis acceptance_spec produirait des faux positifs sur les frames agent-internes légitimes. Je corrige la prose (drift réel, citation roadmap-globale fausse) sans instrumenter la surface. 0 chiffre inventé (#6) · 0 commande VPS (#8).

Bilan. 2 surfaces source alignées S5→S4 · directive Michel INTACTE · 0 édition d'autorité (l'autorité acceptance_spec disait déjà S4) · 0 gate · 0 invention · CI 33 PASS. SIGNAL 071201 → clos.

Session 071201 · FIX de COUVERTURE doc — la fiche CRM omettait son 4e module livré (crm/financement_bancaire) : ajout d'une sous-section dédiée + ligne de table auto-gatée (35 tests · job CI · verbes CLI) · 0 édition d'autorité

Contexte + choix de tâche. ./run_ci.sh au démarrage : 33 PASS · 0 FAIL · 0 SKIP, arbre propre. Roadmap ROADMAP_8_WEEKS_OR_LESS.md intégralement livrée/gatée (24/24 modules 4Big à 100/100, matrice d'acceptance 15/15 in_repo) — Sprint 8 · launch-readiness. Les décisions ouvertes restantes sont des arbitrages produit de Michel (open-decisions-register, ne pas trancher · #5). Tâche retenue : le fallback explicite de la mission (« améliorer la doc d'un AGENT.md ») appliqué à un écart réel, pas cosmétique.

Le finding. La fiche 03_agents/crm/AGENT.md se présente comme le recensement des « Livrables CRM réellement produits » mais n'en documentait que 3 (trio pipeline workflow_vente/dossier_vente/ commissions). Or 05_deliverables_mvp/crm/ héberge un 4e module gatéfinancement_bancaire (parcours hypothécaire RD · gate check 4 conditions · 35 tests · job crm-financement-bancaire-tests) — absent de TOUTES les 13 fiches agents (grep confirmé), y compris de sa fiche propriétaire naturelle. Ce n'était pas une inexactitude (les 3 lignes existantes sont justes, cf. fiche-accuracy-audit-closed) mais une incomplétude : un livrable réel sans surface de doc côté agent.

Pourquoi il était hors du trio (et le reste). Le trio partage une source unique (workflow_vente_spec.json + rbac_50_roles.json) — c'est la propriété « cross-cohérents » que décrit la prose du trio, et que le gate check_readme_claims.sh:725 encode en figeant « Total CRM : 81 tests » aux 3 suites du pipeline. financement_bancaire ne partage pas cette source (piloté par DIRECTIVE_FINANCEMENT_BANCAIRE_COMPLET_20260803.md), donc l'ajouter comme 4e ligne du trio aurait été faux sémantiquement et cassé le gate (regex Total CRM : **N tests**). D'où une sous-section séparée, le trio et son agrégat gaté laissés intacts.

Sourcing (zéro invention · #6). Sprint 4 · roadmap L52 lu dans l'autorité roadmap-globale qa/acceptance/acceptance_spec.json:71-84 (byte-gaté ; evidence_modules inclut crm/financement_bancaire sous S4) — pas le « Sprint 5 » du README module, qui est le cadre agent-interne (agent-internal-vs-roadmap-sprint-frames). Nombre de tests 35 lu dans regression_plan.json (test_methods, source faisant autorité). CLI financement_bancaire_gen.py build|validate vérifié depuis les subparsers.

Auto-gatage confirmé — le finding devient opposable au merge. La nouvelle ligne de table est captée par trois gates indépendants de check_readme_claims.sh (résolution par cible de lien, crm/financement_bancaire ∈ auth) : (a) cellule Tests 35 == source (35), (b) job CI crm-financement-bancaire-tests == ci.yml, (c) verbes CLI build|validate == subparsers. Une future dérive du compte/job/CLI mordra désormais. ./run_ci.sh final : 33 PASS.

SIGNAL (surfacé, non tranché · SURFACE don't rewrite). Le README du module écrit « Sprint 5 · roadmap L52 » — pairing interne étrange (L52 est une ligne Sprint 4) : soit le cadre agent-interne, soit une micro-dérive du label roadmap-global vs l'autorité acceptance_spec (S4). Non édité (le module README n'est pas ma cible ; le piège des deux cadres de sprint exige une édition spec+regen coordonnée, cf. agent-internal-vs-roadmap-sprint-frames) — consigné ici pour un passage ultérieur.

Bilan. 1 fiche agent enrichie (sous-section + 1 ligne auto-gatée) · 0 édition d'autorité · 0 gate ajouté (surface déjà couverte par row_re/job/CLI · #5) · 0 chiffre inventé · CI 33 PASS.

Session 051154 · Attestation des DENTS des suites de MODULE — BATCH 4 (FINAL) : les 7 dernières suites → 25/25 prouvées · 7 BITES · 0 TOOTHLESS · arbre byte-pristine · 0 édition de production

Contexte + choix de tâche. ./run_ci.sh au démarrage : 33 PASS · 0 FAIL · 0 SKIP, arbre propre, roadmap ROADMAP_8_WEEKS_OR_LESS.md intégralement livrée/gatée — nous sommes au Sprint 8 · QA : regression tests exhaustifs. Continuation et clôture de l'axe des DENTS des suites de MODULE (module-suite-teeth-mutation) : les batches 034144/041149/044153 ont prouvé 18/25, laissant les 7 dernières non-couvertes (rbac-aggregate, portails, audit-4big, demo-scenario, regression, deploy-runbook, acceptance) — non-couvertes ≠ édentées, juste non-prouvées. Ce batch les couvre toutes → 25/25. Axe distinct des 8 gates STATIQUES (8/8, 031134) : une suite qui rechargerait out/*.json contre elle-même serait édentée = faux vert Sprint 8 comme un gate édenté.

Méthode. Reconnaissance (Explore, read-only) pour cartographier, par module, une ligne de LOGIQUE de générateur reconstruite en mémoire par une assertion. Vérification critique avant de croire l'agent (module-suite-teeth-mutation leçon (b)) : deux mutations proposées étaient INERTES, pas mordantes — and→or sur deploylib/builder.py:61 (not [] and not [] = True → or = True aussi, sortie inchangée) et idem acclib/builder.py:138. Remplacées par des mutations qui font basculer la sortie observée : deploygated_set - mapped_set | (union : missing non-vide, bijective False, asserté par test_coverage_bijective) ; acceptance"exact": want == have != (chaque p["exact"] bascule False, asserté par test_partition_exact_all_sprints). Unicité de chaque cible re-vérifiée par grep -Fc == 1 avant mutation. Harnais /tmp read-only (/tmp/suite_teeth_20260805_batch4.sh) : baseline verte sanity → backup cpstr.replace (assert 1 occurrence) → run de la seule suite du module (working-directory dérivé de ci.yml) → assert exit≠0 → restauration cp + cmp byte-exact ; trap EXIT restaure tout ; jamais git clean/checkout (ci-gate-verification-method).

6 BITES — mutations de LOGIQUE de générateur, sortie re-assertée en mémoire :

  • portails · wslib/builder.py:114 roles_couverts = sum(m["nb_roles"] …) + 1test_roles_restriction recompte le total vs manifest.counts.roles_couverts.
  • audit-4big · q4lib/builder.py:30 "fail": len(below) + 1test_real_build_passes_gate exige totals.fail == 0.
  • demo-scenario · scenlib/builder.py:44 duree = sum(b["duree_min"] …) + 1test_duree_est_somme_des_beats recompte sc["duree_min"] vs somme des beats.
  • regression · reglib/builder.py:44 "test_methods": sum(s["test_methods"] …) - 1test_totals_match_suites recompute la somme vs totals.test_methods.
  • deploy-runbook · deploylib/builder.py:59 gated_set - mapped_set → |test_coverage_bijective exige bijective + missing==[].
  • acceptance · acclib/builder.py:136 "exact": want == have → !=test_partition_exact_all_sprints exige chaque p["exact"] + partition_ok.

1 BITE — sous-classe distincte (validation-de-CONTRAT, pas générateur). rbac-aggregate (rbac/tests/test_rbac.py) n'a pas de builder/out/ : il charge directement le contrat rbac_50_roles.json et y asserte des invariants absolus (schéma, ==50, unicité id/nom, couverture des 5 portails, invariant #6 set_user_permissions). Structurellement non-echo (il ne compare pas un artefact à lui-même — il contraint des constantes dures). Dent prouvée par une dérive du contrat "cible_rbac_roles": 50 → 49, assertée par test_exactement_50_rolesBITE. Honnêteté de portée : axe « contrat » ≠ axe « logique de générateur », signalé comme tel plutôt que forcé dans le même moule.

Verdict : 7 BITES · 0 TOOTHLESS → clôture 25/25 des suites de MODULE mordantes. Combiné aux 8 gates STATIQUES (8/8) et à la reproductibilité des artefacts, le Sprint 8 « regression tests exhaustifs » est attesté sur ses deux axes de DENTS.

Vérifications. Arbre byte-pristine après harnais (git status --porcelain vide, git diff --stat vide). ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP (aucun fichier gaté touché — restauration byte-exact). 0 chiffre inventé (#6) · 0 gate ajouté (prouve les tests existants, n'en crée aucun — #5) · 0 édition de production · aucune commande VPS (#8) · aucun git clean.

Session 044153 · Attestation des DENTS des suites de MODULE — BATCH 3 : mutation-test generator-logic sur 6 nouvelles suites (→ 18/25 prouvées) · 6 BITES · 0 TOOTHLESS · arbre byte-pristine · 0 édition de production

Contexte + choix de tâche. ./run_ci.sh au démarrage : 33 PASS · 0 FAIL · 0 SKIP, arbre propre, roadmap ROADMAP_8_WEEKS_OR_LESS.md intégralement livrée/gatée. Continuation directe de l'axe des DENTS des suites de MODULE (module-suite-teeth-mutation) : les batches 034144 (6) et 041149 (6) ont prouvé 12/25 suites, laissant 13 non-couvertes (≠ édentées, juste non-prouvées). Ce batch 3 en couvre 6 de plus → 18/25. Axe distinct des 8 gates STATIQUES (8/8, 031134) : une suite qui rechargerait out/*.json contre elle-même serait édentée = faux vert Sprint 8.

Méthode. Reconnaissance (Explore, read-only) pour cartographier, par module, une ligne de LOGIQUE de générateur (calcul/règle, jamais un octet d'artefact) qu'une assertion unittest reconstruit en mémoire depuis le contrat. Unicité de chaque cible re-vérifiée par grep -Fc == 1 avant mutation. Harnais /tmp read-only (/tmp/suite_teeth_20260805_3.sh) : backup cp -p → remplacement d'octets littéral (str.replace, assert 1 occurrence) → run de la seule suite du module → assert exit≠0 → restauration cp + cmp byte-exact ; trap EXIT restaure tout ; jamais git clean/checkout (ci-gate-verification-method).

6 BITES (mutations de LOGIQUE réelles) :

  • rbac/fixtures_genfixturelib/builder.py:43 if_owner = scope == SCOPE_IF_OWNER == → != → inverse tous les if_owner, test_if_owner_reflete_scope_own (attend 1 if scope=="own" else 0) mord.
  • rbac/userperm_genpermlib/builder.py:43 by_mechanism[mechanism] += 1 → += 2 → total 50→100, test_manifeste_fidele (assertEqual(sum(by_mechanism.values()), 50)) mord.
  • rbac/apply_planapplylib/aggregator.py:174 chaîne roles == cible == up_entries == rp_covered, 2ᵉ == → !=couverture_bijective bascule False, test_consistency_bijective (assertTrue) mord.
  • mobile/app_configmobilelib/builder.py:105 roles = sorted(roles_map.get(key, [])) +reverse=True → ordre inversé, test_roles_allowed_egal_surface_rbac (==sorted(roles_map[portail])) mord.
  • qa/audit_5dqalib/builder.py:60 "fail": all_statuts.count(_FAIL) _FAIL → _PASS → totals fail 0→13, test_verdict_pass_with_open_items (totals == {...,"fail":0,...}) mord.
  • frontend/chat_otoiachatlib/builder.py:114 "roles_couverts": sum(m["nb_roles"] …)sum(1 …) → 44→~5, test_44_roles_couverts (total(44) == roles_couverts) mord.

LEÇON de méthode réappliquée — désambiguïser TOOTHLESS-test vs mutation INERTE. L'Explore avait d'abord proposé mobile mobilelib/builder.py:192 "roles_couverts": sum → len : édenté pour la suite de MODULE car roles_couverts (agrégat manifest) n'est jamais ré-asserté en mémoire par la suite mobile (seuls n_roles/roles_allowed par-onglet le sont). Observation honnête, pas un défaut — on a muté à la place une ligne réellement contrainte (:105 le tri). Idem fixtures :68 doctypes_uniques (non référencé par la suite) → basculé sur :43 if_owner. → Règle confirmée : vérifier que la ligne mutée est bien celle que le test reconstruit avant de croire un verdict.

Portée honnête. 18/25 échantillonnées pour la diversité (inversion booléenne, incrément de compteur, chaîne d'égalité, tri-déterminisme, count de statut, agrégat de rôles). 7 restantes non-couvertes ≠ édentées (rbac agrégé, portails, audit-4big, demo-scenario, regression, deploy-runbook, acceptance) — pas de plafond silencieux.

Vérifications. Après restauration : git status --porcelain vide, arbre byte-exact pristine (6/6 fichiers cmp OK). ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP (inchangé). Harnais /tmp supprimé. 0 gate ajouté (#5 — prouve les tests existants, n'en ajoute aucun) · 0 chiffre inventé (#6) · 0 édition de production · 0 commande VPS (#8) · aucun git clean.

Session 041149 · Attestation des DENTS des suites de MODULE — BATCH 2 : mutation-test generator-logic sur 6 nouvelles suites (→ 12/25 prouvées) · 6 BITES · 0 TOOTHLESS · arbre byte-pristine · 0 édition de production

Contexte + choix de tâche. ./run_ci.sh au démarrage : 33 PASS · 0 FAIL · 0 SKIP, arbre propre. Continuation honnête et directe de l'axe des DENTS des suites de MODULE (module-suite-teeth-mutation) : la session 034144 avait prouvé 6/25 suites, laissant 19 non-couvertes (≠ édentées, juste non-prouvées). Ce batch 2 en couvre 6 de plus → 12/25. Axe distinct des DENTS des 8 gates STATIQUES (8/8, 031134) : une suite qui rechargerait out/*.json contre elle-même serait édentée = faux vert Sprint 8 comme un gate édenté.

Méthode. Reconnaissance (Explore, read-only) pour cartographier, par module, une ligne de LOGIQUE de générateur (calcul/règle, jamais un octet d'artefact) qu'une assertion unittest reconstruit en mémoire depuis le contrat. Unicité de chaque cible vérifiée par grep -Fc avant mutation. Harnais /tmp read-only (/tmp/suite_teeth_20260805_2.sh) : backup cp → mutation de la logique → exécution de la seule suite du module → assertion exit≠0 → restauration cp + cmp byte-exact ; trap EXIT restaure tout ; jamais git clean/checkout (ci-gate-verification-method).

6 BITES (mutations de LOGIQUE réelles) :

  • faisabilite/generatorscorer._score_completude 20 * filled → 20 / filled (sed adressé L57 car la formule récurre L81) → le score 4big tombe < 95, test_complete_brief_scores_publiable mord (exit 1).
  • faisabilite/bancablefinance.derived point d'équilibre math.ceil(pe_brut) → math.floor → 21→20, test_derived_arithmetic_traceable mord.
  • crm/workflow_ventebuilder trans_rows.sort(...) +reverse=True → casse le déterminisme trié, test_transitions_sorted_stable mord.
  • crm/dossier_venteis_submittable seuil >= "1" (voir ci-dessous) mord.
  • crm/financement_bancairegate.apport_requis_usd / 100.0 → * 100.0 → apport ×10 000, test_apport_requis_* mord.
  • publiciste_fmt_usd suppression du .replace(",", " ") (séparateur de milliers = espace) → "USD 150,000" ≠ "USD 150 000" attendu (test L201) mord.

LEÇON de méthode — un verdict « TOOTHLESS » doit être désambiguïsé : (a) test réellement édenté vs (b) mutation comportementalement INERTE pour la donnée du contrat. Deux mutations de ce batch ont d'abord lu TOOTHLESS mais étaient (b), PAS des défauts :

  • dossier_vente >= "1" → > "1" est inerte car le max des doc_status réels = "2" (les deux branches donnent 1). Une mutation qui fait basculer le résultat (>= "3""2">="3" False → 0) mord bien : la suite contraint la règle is_submittable.
  • publiciste min(prices) → max est inerte car le retour de _prix_depuis n'alimente QUE la gate is not None (L116) ; sa valeur min-vs-max n'est jamais affichée (chaque ligne de typologie rend son propre prix, L57). Observation honnête, pas un défaut à corriger (#5/#6) : la sémantique du minimum n'a aucun effet observable en aval → on mute plutôt une logique observée (_fmt_usd). → Règle : confirmer que la mutation change la SORTIE pour la fixture avant de croire un TOOTHLESS.

Portée honnête. 12/25 échantillonnées pour la diversité (arithmétique, seuil booléen, tri-déterminisme, zfill/format, dédup, séparateur) ; 13 restantes non-couvertes ≠ édentées (rbac-agrégat/fixtures/userperm/ applyplan, portails, audit-5d, chat-otoia, audit-4big, demo-scenario, regression, deploy-runbook, acceptance, mobile-app-config) — pas de plafond silencieux.

Vérifications. Après restauration : git status propre, arbre byte-exact pristine (6/6 fichiers cmp OK). ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP (inchangé — aucun fichier gaté modifié en net). 0 gate ajouté (#5 — prouve les tests existants, n'en ajoute aucun) · 0 chiffre inventé (#6) · 0 édition de production · 0 commande VPS (#8) · aucun git clean.

Session 034144 · Attestation des DENTS des suites de test de MODULE — mutation-test generator-logic (6 suites échantillonnées, chacune doit sortir NONZERO sur une dérive de LOGIQUE de son générateur, pas d'écho d'artefact) → 6 BITES · 0 TOOTHLESS · arbre byte-pristine · 0 édition de production

Contexte + choix de tâche. ./run_ci.sh au démarrage : 33 PASS · 0 FAIL · 0 SKIP, arbre propre, roadmap ROADMAP_8_WEEKS_OR_LESS.md intégralement livrée/gatée. Vérifications d'entrée : les 3 chiffres launch-readiness rechargent à l'identique depuis les artefacts commités (recompute python3 indépendant, aucune valeur saisie à la main) — quality_report 24 modules · tous == 100 · coverage.ok · regression_run suites 24 · ran 624 · passed 624 · 0/0/0 · acceptance_matrix 15/15 in_repo · verdict True · 36 ci_job · run_ci --list 33 = 8 statiques + 25 suites. Un balayage read-only fan-out (README de module + docstrings des *_gen.py/*lib + DIRECTIVE↔module) n'a remonté aucune dérive prose (surfaces contenu saturées, #5). Axe distinct et jamais couvert en suite : hier (031134) a prouvé que les 8 gates STATIQUES mordent (8/8) ; personne n'a jamais prouvé que les 25 suites unittest de MODULE mordent sur une dérive de LOGIQUE de générateur. Une suite qui ne ferait que recharger out/*.json et l'asserter contre lui-même serait édentée (faux vert Sprint 8 « regression tests exhaustifs ») exactement comme un gate édenté. Constat encourageant préalable : les suites reconstruisent en mémoire depuis le contrat (builder.build_bundle(contract)), donc elles devraient contraindre la logique — restait à le prouver.

Méthode (verify-uncovered-before-gating appliquée à l'inverse — prouver que la protection MORD). Harnais unique hors dépôt (/tmp, jamais commité), par suite : backup cp → mutation ciblée de la LOGIQUE du générateur → run de la SEULE suite du module → assert exit≠0 → restore byte-exact cp → cmp. Trap EXIT restaurant tout même en cas d'échec. Chaque mutation = une vraie erreur de calcul/structure que le générateur pourrait introduire (cible re-vérifiée unique sur l'arbre courant AVANT via grep -Fc), jamais un octet d'artefact ni un commentaire :

# Suite (module) Mutation generator-logic injectée Verdict
1 crm-commissions finance.py : commission base * tauxbase + taux BITES exit=1
2 pie-manifest pielib/builder.py : _statut inversé (gateda_construire) → compteurs faussés BITES exit=1
3 fiscal-ecf ecflib/ncf.py : e-NCF zfill(10)zfill(9) (rompt le format 13 car. Ley 32-23) BITES exit=1
4 rbac-roleprofile profilelib/builder.py : nb_roles = len(role_names)+ 1 BITES exit=1
5 seo seolib/keywords.py : dédup neutralisée (if key in seen and False:) → doublons BITES exit=1
6 legal/confotur cflib/builder.py : options entites jointes "\n" → `" "` (mauvais séparateur Frappe)

Résultat : 6 BITES · 0 TOOTHLESS. Chaque suite échantillonnée oppose au merge une dérive réelle de la logique de son générateur — aucune n'est un simple miroir d'artefact. Combiné à hier, l'opposabilité au merge est prouvée sur 8 gates statiques + 6 suites de module.

Portée honnête (pas de plafond silencieux). Échantillon de 6 des 25 suites de module, choisi pour la diversité des styles d'assertion (calcul monétaire · compteurs de statut · format réglementaire · métadonnée de comptage · dédup · sérialisation d'options). Les 19 suites non mutées ce jour (publiciste, rbac(agrégée)/fixtures/userperm/applyplan, faisabilite-gen, bancable, workflow-vente, dossier-vente, financement-bancaire, portails, audit-5d, chat-otoia, audit-4big, demo-scenario, regression, deploy-runbook, acceptance, mobile-app-config) restent à attester en session ultérieure — non couvertes ≠ édentées, juste non prouvées aujourd'hui.

Discipline. Attestation read-only par conception (harnais en /tmp, mutations restaurées cp depuis backup + cmp byte-exact · jamais git clean/git checkout — cf. interdit CLAUDE.md · ci-gate-verification-method). 0 gate ajouté (#5 — on ne teste pas, on prouve les tests existants) · 0 chiffre inventé (#6) · 0 édition de production (seul ce journal change). Axe dents des suites de module, distinct de l'axe dents des gates (031134) et des attestations de contenu.

Vérifications. git status --porcelain vide (arbre byte-pristine post-harnais) · cmp backup↔restauré pour les 6 fichiers touchés = identiques · ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP (inchangé). Aucune commande VPS (#8) · aucun git clean.

Session 031134 · Attestation launch-readiness des DENTS du gate — mutation-test des 8 gates statiques (chacun doit sortir NONZERO sur sa dérive) → 8 BITES · 0 TOOTHLESS · arbre byte-pristine · 0 édition de production

Contexte + choix de tâche. ./run_ci.sh au démarrage : 33 PASS · 0 FAIL · 0 SKIP, arbre propre, roadmap ROADMAP_8_WEEKS_OR_LESS.md intégralement livrée/gatée (8 sprints · 7 critères ). Les ~15 dernières sessions ont saturé les axes d'audit contenu : cross-artefact, dérive free-text (docstring↔code, inventaire↔fs), attestation des 4 chiffres stakeholder, des 18 citations file:line du registre. Axe distinct et non couvert en suite depuis le 2026-08-02 : prouver que les gates ont encore des DENTS. run_ci prouve qu'ils PASSENT sur arbre propre — il ne prouve jamais qu'ils ÉCHOUENT sur une vraie dérive. Un gate devenu édenté (toujours vert) est pire qu'absent : un faux vert de launch-readiness. La mémoire guard-constraints-flags-usage-not-mention notait la dernière suite-mutation-test à 7/7 ROUGE le 2026-08-02 — or depuis : +1 gate (8ᵉ check-mobile-workflow, 001109) et ~3 jours d'éditions. Aucune session n'a re-prouvé la suite entière à 8. Valeur honnête du jour : Sprint 8 « regression tests exhaustifs » — attester les dents des 8, corriger tout édentement.

Méthode (discipline verify-uncovered-before-gating appliquée à l'inverse — prouver que la protection MORD). Harnais unique hors dépôt (/tmp, jamais commité), par gate : backup→mutation ciblée→run gate→assert exit≠0→restore byte-exact, avec trap EXIT restaurant tout même en cas d'échec. Une mutation = l'exacte classe de bug que le gate est censé opposer au merge (aucune liste en dur — chaque cible re-vérifiée sur l'arbre courant AVANT) :

# Gate Mutation injectée Verdict
1 guard_constraints usage chemin interdit /var/www/html/static/… (écriture · #4) BITES exit=1
2 validate_json JSON malformé (GARBAGE_NOT_JSON appendé à un out/*.json) BITES exit=1
3 check_docs lien Markdown interne cassé (./inexistant-xyz-123.md) BITES exit=1
4 check_artifacts out/MANIFEST.json commité ≠ build frais (1 octet) BITES exit=1
5 check_regression regression_run.json passed:25→24 vs run frais BITES exit=1
6 check_ci_integrity 1 job (pie-manifest-tests) retiré de gate.needs BITES exit=1
7 check_readme_claims compte d'agents dérivé 13→12 dans README BITES exit=1
8 check_mobile_workflow secrets. réintroduit dans un if: de job (SKIP silencieux) BITES exit=1

Résultat : 8 BITES · 0 TOOTHLESS. Chaque gate statique oppose encore au merge la classe de dérive qu'il déclare protéger — aucun édenté malgré 3 jours d'éditions. La mutation #8 est précisément l'anti-pattern 234104 (contexte secrets indisponible en jobs.<id>.if → build SKIP silencieux le jour où Michel fournit EAS_TOKEN) : opposabilité au merge confirmée.

Discipline. Attestation read-only par conception (harnais en /tmp, mutations restaurées cp depuis backup · jamais git clean/git checkout — cf. interdit CLAUDE.md · ci-gate-verification-method). 0 gate ajouté (#5 — on ne teste pas, on prouve les tests existants) · 0 chiffre inventé (#6) · 0 édition de production (seul ce journal change). Distinct des attestations 234104/004112 (contenu) : axe dents des gates, jamais couvert en suite complète à 8.

Vérifications. git status --porcelain vide (arbre byte-pristine post-harnais) · diff backup↔restauré pour les 8 fichiers touchés = identiques · ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP (inchangé). Aucune commande VPS (#8) · aucun git clean.

Session 024134 · Attestation launch-readiness du OPEN_DECISIONS_REGISTER (18 citations file:line + 2 numériques d'en-tête re-prouvées) → 1 FIX de dérive latente : dé-figeage des 2 compteurs mobiles gelés dans le doc · 0 gate ajouté · 0 édition d'autorité

Contexte + choix de tâche. ./run_ci.sh au démarrage : 33 PASS · 0 FAIL · 0 SKIP, arbre propre, roadmap intégralement livrée/gatée. Le OPEN_DECISIONS_REGISTER.md a été promu découvrable hier (session 021124) mais son contenu — 18 citations file:line vers des sources d'autorité (CLAUDE.md, directives datées, pie_spec.json, gate.py, journaux) — n'avait jamais été re-vérifié indépendamment contre les numéros de ligne courants. Un punch-list launch-readiness dont les ancres pointent à côté est trompeur. Valeur honnête distincte : attester chaque citation, et corriger toute dérive réelle du doc lui-même (le registre est notre doc worker, pas une source d'autorité — l'éditer pour l'exactitude est un travail worker légitime, distinct de SURFACE-don't-rewrite qui protège les autorités).

Attestation (read-only d'abord). Les 18 citations re-prouvées exactes au numéro de ligne :

  • D-01 (PIE) : pie_spec.json:24 (contratslegal/confotur) · siblings null :22,27,28,29,30 · out/MANIFEST.json:24 · README.md:48 · out/pie_manifest.json:165-166 · legal/confotur/README.md:8-17. Toutes exactes.
  • D-02 (financement) : DIRECTIVE_FINANCEMENT…:206-210 + :275-279 (couche ## PRÉCISION audit-IA) · finlib/gate.py:142-145 (_cond_validation_wag teste encore wag_validated_by) · financement_spec.json:157. Toutes exactes.
  • D-03/D-04 (inventaire) : AGENTS_EXISTING_ASSETS.md:102/127/128 · CLAUDE.md:28 · activity_log/2026-08-03.md:1245-1250/:1251-1253 · filesystem (chat.py, projets_editor.py absents). Toutes exactes.
  • D-05 (mobile) : mobile_spec.json (a_confirmer) · out/app_config.json (bundleIdentifier/package null) · DIRECTIVE_MOBILE_STORES:15-16 (com.otov7.app). Toutes exactes.

FIX — dérive latente réelle (en-tête, ligne 9). Le > **Portée** gelait deux compteurs activement mobiles : 33/33 gates et 24 modules 4Big 100/100.

  • 33 : le compte de gate.needs est passé 32→33 il y a 2 jours (b7ffb64) ; le README refuse délibérément de le figer (« gate.needs dérivé de ci.yml, sans liste en dur », README.md:28). Le registre était le seul doc à le geler → à contre-courant de la discipline projet.
  • 24 : historique 21→22→24 (commentaires de check_readme_claims.sh) ; ce chiffre est déjà gaté (recomputé depuis quality_report.json totals pour le README + audit_4big/README.md). Le registre en était une 3ᵉ copie ungated → dérive silencieuse au prochain module ajouté.
  • Correction (dé-figeage, pas gate #5) : reformulé en qualitatif — « tous les jobs de gate.needs passent · tous les modules 4Big au seuil 100/100, verdict PASS » + pointeur vers les sources dérivées-et-gatées (README ## État courant + quality_report.json). Aligne le registre sur la philosophie no-hardcoded-count du README ; discipline single-source, pas un gate redondant.

Discipline. Attestation confirmée AVANT tout edit (33/33 gates · 24 modules 100/100/PASS re-dérivés : gate.needs→33 · quality_report.json totals→{modules:24, pass:24, min/max:100, verdict:PASS}). 0 édition d'autorité (CLAUDE.md, directive, pie_spec.json, gate.py intouchés) · 0 gate ajouté (#5) · 0 chiffre inventé (#6, on retire deux chiffres, on n'en ajoute aucun) · 1 seule ligne du registre modifiée. Items D-01..D-05 inchangés (la provenance « consolidation » reste 014119 — ce reword ne touche aucun item).

Vérifications. grep compteurs N/N/N modules résiduels dans le registre → aucun (clean). ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP. Aucune commande VPS (#8) · aucun git clean.

Session 021124 · OPEN_DECISIONS_REGISTER rendu découvrable — indexé dans la Navigation README + pointeur « État courant » (le punch-list launch-readiness n'était référencé nulle part) → 2 éditions README, 0 édition d'autorité

Contexte + choix de tâche. ./run_ci.sh au démarrage : 33 PASS · 0 FAIL · 0 SKIP, arbre propre, roadmap intégralement livrée/gatée. La session 014119 (juste avant) a créé 05_deliverables_mvp/OPEN_DECISIONS_REGISTER.md — une punch-list launch-readiness consolidée « pour que Michel la trouve ». Mais elle ne la référençait de nulle part : grep -rl OPEN_DECISIONS_REGISTER --include=*.md ne renvoyait que la ligne du journal 014119. Le README ## Navigation indexe pourtant son sibling GAP_ANALYSIS_SPRINT1.md ligne 24. Un punch-list que personne ne peut atteindre est de valeur nulle : fermer cette boucle de découvrabilité est la valeur honnête distincte du jour (ni gate redondant #5, ni re-surfaçage — le registre consolide déjà).

Vérification AVANT promotion (anti-invention #6 · on ne met pas en avant un doc non re-vérifié). Re-prouvé indépendamment les claims-clés du registre au filesystem/source :

  • D-01 : pie_spec.json:24 attribue bien contrats (Promesa · Fideicomiso · HOA) à legal/confotur — RÉEL.
  • D-03 : CLAUDE.md:28 liste chat.py ; /opt/oto/otoia/capabilities/chat.py absent — RÉEL.
  • D-04 : AGENTS_EXISTING_ASSETS.md:127 « projets_editor.py API en place » ; aucun projets_editor.py sous /opt/oto/config — RÉEL (claim runtime VPS non réfutable, #8). Registre jugé fidèle → promotion légitime.

Éditions (2, README seul).

  1. ## Navigation — nouvelle ligne juste après GAP_ANALYSIS_SPRINT1.md, pointant le registre (description : punch-list décisions produit ouvertes qui appartiennent à Michel · sourcée, ne tranche pas).
  2. ## État courant (sourcé) — bullet « Décisions produit ouvertes » : CI verte ≠ zéro décision en attente → pointe le registre. Sans compte chiffré en dur (le nombre d'items dériverait · #6) : pointeur qualitatif uniquement.

Discipline. SURFACE, don't rewrite : 0 édition de CLAUDE.md, d'une directive datée, d'un pie_spec.json/gate.py, ni du registre lui-même. Aucun gate ajouté (#5). Les 2 lignes README sont de la prose de navigation (pas de claim numérique gaté).

Vérifications. ci/check_readme_claims.shvert (chiffres README inchangés, fidèles). ci/check_docs.shvert (nouveaux liens internes valides). ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP. 0 chiffre inventé (#6) · 3 claims re-prouvés avant promotion · 0 édition d'autorité · aucune commande VPS (#8) · aucun git clean.

Session 014119 · Registre des décisions ouvertes — consolidation sourcée des arbitrages produit surfacés au fil de l'eau (Sprint 8 launch-readiness) → 1 livrable-doc neuf · 0 édition d'autorité

Contexte + choix de tâche. ./run_ci.sh au démarrage : 33 PASS · 0 FAIL · 0 SKIP, arbre propre. Roadmap intégralement livrée/gatée. Les 6 dernières sessions (200738 08-03 → 011114 08-05) ont saturé l'audit read-only (inter-artefacts, dérive free-text, attestation stakeholder, 8ᵉ gate mobile). Reproduire une 7ᵉ passe read-only aurait été à faible valeur / redondant (#5). Valeur honnête distincte du jour : les items réellement ouverts (arbitrages de périmètre produit + dépendances runtime hors dépôt) sont éparpillés dans les journaux de session et n'existaient nulle part de façon consolidée et découvrable pour Michel — un vrai trou de launch-readiness Sprint 8. J'ai créé 05_deliverables_mvp/OPEN_DECISIONS_REGISTER.md : une punch-list unique, 100 % sourcée, qui regroupe (ne tranche pas, ne réécrit aucune source d'autorité).

Vérification indépendante de CHAQUE signal AVANT consolidation (anti-invention #6 · deux Explore parallèles + spot-check manuel). Aucun item repris de mémoire sans re-preuve :

  • D-01 PIE contratslegal/confotur : REAL. pie/manifest/pie_spec.json:24 attribue contrats (Promesa · Fideicomiso · HOA) à legal/confotur ; or ce module ne produit que le DocType demande CONFOTUR (legal/confotur/README.md:8-17). Incohérence interne confirmée : tous les autres downstreams sans générateur portent "module": null (pie_spec.json:22,27-30), contrats est le seul attribué à un module qui ne le génère pas. Arbitrage de périmètre.
  • D-02 Financement condition #4 WAG→audit IA : REAL. Directive ## PRÉCISION (DIRECTIVE_FINANCEMENT_..._20260803.md:275-279) supersede wag_validated_by par audit.decision=='APPROVED' signé ; le module ship encore l'ancienne couche (finlib/gate.py:142-145 + financement_spec.json:157). Dépend d'un agent auditeur hors dépôt.
  • D-03 chat.py absent / D-04 projets_editor.py absent : REAL, filesystem confirmé (ni .py ni .pyc sous /opt/oto), déjà surfacés session 20260803_133718 (2026-08-03.md:1245-1253) → registre = pointeur anti-doublon (#5), pas re-surfaçage.
  • D-05 bundle id mobile : soupçon INVALIDÉ → 🟢 VÉRIFIÉ-SANS-SUITE. Le renommage OTOV7→« OTO Enterprise OS » ne touche pas le bundle : bundleIdentifier/package quarantinés null · a_confirmer, découplés du nom d'app (mobile_spec.json, out/app_config.json). Consigné pour ne pas ré-ouvrir. Faux-positif écarté (#6).

Discipline. SURFACE, don't rewrite respecté : 0 édition de CLAUDE.md, d'une directive datée, ou d'un pie_spec.json/gate.py (les toucher serait invention de périmètre ou réécriture d'autorité). Le registre est un doc-pointeur volontairement non-gaté (chaque ligne cite sa source file:line, contenu 100 % dérivé — faible surface de dérive), au même titre que daily_reports.

Vérifications. ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP (inchangé — aucun fichier gaté touché). 0 chiffre inventé (#6) · 1 faux-positif écarté (bundle id) · 4 signaux re-prouvés indépendamment avant consolidation · 0 édition d'autorité · aucune commande VPS (#8) · aucun git clean.

Session 001109 · Nouveau gate check-mobile-workflow — la structure de mobile-build.yml devient opposable au merge (surface mutation-testée UNGATED, 1er contrôle) → 1 gate ajouté + câblé · 32→33 jobs

Contexte + choix de tâche. ./run_ci.sh au démarrage : 32 PASS · 0 FAIL · 0 SKIP, arbre propre. Roadmap ROADMAP_8_WEEKS_OR_LESS.md intégralement livrée/gatée (8 sprints). Les 4 sessions du 08-04 (221055, 224101, 231101, 234104) avaient saturé les surfaces d'audit read-only (inter-artefacts, dérive non-gatée, attestation stakeholder) puis durci mobile-build.yml (pattern preflight-output, portable GitHub/Gitea). Cette dernière édition a laissé une queue : son propre log notait « Aucun gate ne linte la structure de mobile-build.yml (check-ci-integrity ne verrouille QUE ci.yml) ». Autrement dit le correctif de portabilité qu'elle venait de poser pouvait régresser en silence — exactement la classe « marche dans ma tête, casse le jour où on en a besoin » (mémoire mobile-build-secrets-if-gating : « workflow HORS gate.needsrun_ci won't catch a regression »). La valeur honnête du jour : fermer cette queue en rendant la structure de mobile-build.yml opposable au merge. NON redondant (#5) : c'est le premier contrôle de ce fichier.

Preuve d'absence de couverture AVANT d'ajouter le gate (discipline verify-uncovered-before-gating). Mutation : réintroduire l'exact anti-pattern corrigé en 234104secrets.EAS_TOKEN != '' dans un if: au niveau job. Résultat : 7/7 gates statiques restent VERTS. Surface confirmée non gatée (ni check_ci_integrity — scope CI=ci.yml — ni validate_json — scope .json). Fichier byte-restauré aussitôt (cp depuis backup tmp · jamais git clean).

Livrable : ci/check_mobile_workflow.sh (8ᵉ gate statique) + câblage. Le gate lit des FAITS dans le fichier (jamais une liste à la main · #6), 4 invariants :

  • MOB-1 · bien-formé — existe · name:/on:/jobs: + 3 jobs (preflight, build-ios, build-android) ; parse YAML strict exigé si PyYAML dispo, sinon awk seul (runner sans pip → best-effort, jamais un faux vert : MOB-2/3/4 en sont indépendants).
  • MOB-2 · portabilité0 secrets. dans un if: de job (^ if:). Le contexte secrets n'est pas exposé dans jobs.<id>.if (table d'availability GitHub Actions) → sur act/Gitea il s'évaluerait vide → SKIP silencieux. C'est la régression 234104 verrouillée.
  • MOB-3 · activation différée (#6/#8) — chaque job de build (run: eas build) est gaté sur needs.preflight.outputs.has_token → SKIP tant que le token absent, donc le workflow ne rend jamais le CI rouge avant activation Michel.
  • MOB-4 · contrat d'outputspreflight déclare has_token/has_repo, chaque needs.preflight.outputs.<X> référencé est déclaré (0 dangling) et chaque output déclaré est alimenté par un echo …>>$GITHUB_OUTPUT.

Bug corrigé pendant l'écriture (auto-vérification). 1er jet : preflight était classé « job de build » car un commentaire # … eas build … (l.108, entre deux jobs) était attribué au job précédent par le parseur awk. Corrigé en exigeant run:[[:space:]]*eas build (commande réelle, pas une mention). Après correctif : gate VERT sur le fichier propre.

Mutation-testing du gate lui-même (5 mutations · toutes ROUGES, fichier propre re-VERT). (1) YAML cassé (indent) · (2) secrets. en if: de job · (3) if: du job build-ios supprimé · (4) output fantôme référencé (has_tokenX) · (5) output has_repo non alimenté. Chaque mutation → exit 1 ; cp backup → gate re-VERT.

Câblage (garde check-ci-integrity INV-A/B vert). Job check-mobile-workflow ajouté à ci.yml (steps bash ci/check_mobile_workflow.sh) et à gate.needs. check_ci_integrity reconnaît le nouveau ci/*.sh comme câblé (lancé par un job du gate + il source lib.sh) → INV-A (gate.needs == tous jobs non-manuels) et INV-B (tout ci/*.sh câblé) restent verts. run_ci.sh (dérivé de gate.needs, sans liste en dur) l'inclut automatiquement → passage **32→33 jobs (8 gates statiques

  • 25 suites)**, zéro édition de run_ci.sh (#6, exactitude structurelle).

Doc (traçabilité, pas de dérive). ci/README.md : ligne §1 pour check-mobile-workflow, sous-section §2 détaillée (4 invariants + les 5 mutations), et nuance ajoutée à « Second workflow » (les jobs restent hors gate de merge, mais la structure du fichier est désormais gatée). Convention ci-readme-table-detail-in-section2 respectée : ligne §1 courte, détail en §2. Les mentions historiques « 7 gates re-verts » (§2, records de mutations passées) restent exactes en passé ; le nouveau record dit 8ᵉ gate.

Vérifications. bash ci/check_mobile_workflow.sh → VERT (4 invariants) ; 5 mutations → ROUGE ; python3 yaml.safe_load sur ci.yml → valide ; check_ci_integrity → VERT (câblage intègre) ; ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP. 1 gate ajouté (NON redondant : 1re couverture d'un fichier mutation-testé ungated · pas un doublon #5) · 1 job CI câblé · 0 nouveau module · 0 chiffre inventé (#6) · 0 édition de production hors CI · aucune commande VPS (#8) · aucun git clean.


Session 004112 · Currency du canal stakeholder daily_reports au jalon 33 jobs + réattestation indépendante → CLEAN · 0 édition de production

Contexte + choix de tâche. ./run_ci.sh au démarrage : 33 PASS · 0 FAIL · 0 SKIP, arbre propre. Roadmap intégralement livrée+gatée. La session 001109 (même jour, plus tôt) a fait franchir au gate de merge le cap 32 → 33 jobs (8ᵉ gate statique check-mobile-workflow). Or le canal roadmap-facing daily_reports/ s'arrêtait au 2026-08-04 en affichant encore « 32 PASS ». Les surfaces de dérive read-only étant saturées (re-scan = redondant #5), la valeur honnête du jour est de rendre le canal stakeholder fidèle au point courant — discipline two-logging-channels : garder les deux canaux, chaque figure sourcée d'un artefact commité.

Vérification préalable — pas de dérive présent-tense « 32 » résiduelle. Balayage des surfaces present-tense (README.md, ci/README.md) : aucun décompte de jobs figé en dur (le nombre est dérivé à l'exécution par run_ci.sh --list, jamais retranscrit). Les nombreux « 32 » des logs/daily_reports antérieurs sont des snapshots datés (vrais à l'époque) → classe historical-repro = KEEP (prose-facts-vs-numeric-drift), on n'y touche pas. Le « 7 gates statiques » de ci/README.md l.49 décrit l'état avant l'ajout du 8ᵉ gate (narratif du mutation-test préalable) → exact en passé, KEEP.

Livrable : 05_deliverables_mvp/daily_reports/2026-08-05.md. Snapshot stakeholder consignant le jalon 33 jobs (= 8 gates statiques + 25 suites) + réattestation. Chaque chiffre recomputé python3 indépendamment des artefacts commités (aucune valeur saisie à la main · #6), tous concordants avec la table :

  • quality_report.json : 24/24 modules == 100 · coverage.ok=true · ci==registry==24 · verdict PASS.
  • regression_run.json.totals : 24 suites · 624 ran · 624 passed · 0/0/0 · verdict PASS.
  • acceptance_matrix.json : 15/15 status=in_repo · verdict True · ci_job ×36 (niché matrix[].modules[]).
  • Structure CI : run_ci.sh --list = 33 jobs = 8 statiques + 25 suites · ls ci/*.sh (hors lib.sh) = 8.

Vérifications. ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP (inchangé — livrable prose, hors check_artifacts/audit_4big). Liens Markdown du rapport résolus depuis daily_reports/ : ../qa/… (artefacts), ../../run_ci.sh, ../../.gitea/…, ../../ci/…check_docs VERT. 0 chiffre inventé (#6) · 0 édition de production · aucune commande VPS (#8) · aucun git clean.


Session 011114 · Double sweep read-only sur les DEUX classes de dérive free-text ungated (docstring↔code des 22 générateurs · inventaire↔filesystem) → CLEAN · 0 édition de production · faux « nouveau finding » écarté (anti-invention #6)

Contexte + choix de tâche. ./run_ci.sh au démarrage : 33 PASS · 0 FAIL · 0 SKIP, arbre propre. Roadmap ROADMAP_8_WEEKS_OR_LESS.md intégralement livrée+gatée (8 sprints · 7 critères de succès ). Les deux sessions du jour ont déjà (a) ajouté le 8ᵉ gate statique check-mobile-workflow (001109, 32→33 jobs) et (b) mis le canal stakeholder daily_reports au jalon 33 jobs avec réattestation des 4 chiffres (004112). Les surfaces d'audit inter-artefacts / dérive gatée / attestation sont saturées (re-scan = redondant #5). La valeur honnête restante : re-vérifier les deux classes de dérive free-text NON gatées que la mémoire marque comme récurrentes/non-fermées, avant d'envisager quoi que ce soit — discipline verify-uncovered-before-gating (prouver l'état AVANT d'agir) + anti-invention #6.

Sweep 1 — docstring↔code des 22 générateurs (docstring-vs-code-drift, classe RECURRING/not-closed). Pour chacun des 22 *_gen.py de 05_deliverables_mvp/* : comparaison du docstring/en-tête (inputs annoncés · fichiers out/ produits · fallbacks) au code réel (argparse · open(...,"w") / json.dump / write_text · branches de fallback). Résultat : 22/22 CLEAN — chaque docstring décrit exactement les sous-commandes, les inputs et les artefacts écrits. Spot-check manuel des deux générateurs multi-sorties les plus risqués (au-delà de la lecture agent) :

  • demo/scenarios/demo_scenario_gen.py — docstring l.20/l.32 annonce run_sheet.json + run_sheet.md + MANIFEST.json → code écrit bien les 3 (l.197-199 JSON, l.203-204 .md). ✓
  • faisabilite/bancable/bancable_gen.py — docstring 50_financier_bancable/{fr,en,es}.md → écrits l.171-173. ✓ Aucune régression du type « fallback §5.3 fantôme » (publiciste) ni « output omis » (demo run_sheet.md) qui avaient été corrigés antérieurement : la classe reste propre.

Sweep 2 — inventaire AGENTS_EXISTING_ASSETS.md ↔ filesystem /opt/oto (inventory-vs-filesystem-drift). Extraction de tous les chemins /opt/oto/* cités (38 uniques), classification SRC-OK / PYC-ONLY / MISS (avec check __pycache__) :

  • 8 PYC-ONLY (oto_agent_faisabilite, oto_orchestrateur_faisabilite, oto_module_{etudes_faisabilite_real,execution_faisabilite,faisabilites_pages,faisabilites_viewer,mobile_api,plans_ifc}) → tous déjà annotés « (compilé pyc) » dans le doc : convention interne cohérente, 0 dérive.
  • 2 MISS : otoia/capabilities/chat.py (absent vs CLAUDE.md §Architecture cible qui le liste) et config/projets_editor.py (§13, « API GET/POST déjà en place » — sibling projets_config.json présent, lui). Vérification anti-invention #6 AVANT de flaguer : grep -rn interne → les deux MISS sont DÉJÀ surfacés à Michel par la session 133718 du 2026-08-03 (log l.1245/1284/1289-1295 : claims VPS-externes / non-réfutables laissés ouverts, chat.py ≠ le module chatbot-lead distinct). Donc PAS un nouveau finding — re-signaler serait redondant (#5). Le reste (containers Docker, Speckle, systemd, GSC) est runtime, non réfutable depuis le dépôt → hors périmètre worker (#8). SURFACE, don't rewrite respecté : 0 édition de l'inventaire (le fichier peut viser un runtime container-side ; un « fix » unilatéral serait une invention).

Conclusion honnête. Les deux seules classes de dérive free-text ungated sont stables et propres à ce jalon ; la dimension chiffres-README est densément gatée (check_readme_claims = 27 claims / 16 modules + fiches). Rien à corriger, aucun gate à ajouter (ajouter serait un doublon #5 ; par ailleurs un gate lisant /opt/oto serait non-portable — le runner Gitea n'a pas ce filesystem, cf. ci-gate-verification-method). Session read-only volontaire, dans la lignée des attestations 234104/004112, mais sur un axe distinct (les 2 classes free-text, pas les 4 chiffres stakeholder ni le cross-artefact).

Vérifications. ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP (inchangé — aucun fichier gaté touché). 0 chiffre inventé (#6) · faux nouveau-finding écarté par grep interne · 0 édition de production · 0 gate ajouté (anti-redondance #5) · aucune commande VPS (#8) · aucun git clean.

Session 054200 · Currency du canal STAKEHOLDER : consigner la clôture des DEUX axes de « dents » dans daily_reports/2026-08-05.md · 0 édition de production

Contexte + choix de tâche. ./run_ci.sh au démarrage : 33 PASS · 0 FAIL · 0 SKIP, arbre propre. Roadmap intégralement livrée+gatée côté repo (Sprint 8 buffer). Les surfaces de dérive usuelles sont saturées, donc j'ai d'abord cherché une vraie régression, pas une attestation redondante (#5) :

  1. Chasse docstring↔code drift (classe documentée RÉCURRENTE/non-close — a déjà trouvé le « fallback §5.3 fantôme » publiciste + l'output run_sheet.md omis). Balayage des 21 générateurs (docstring/commentaires vs fichiers réellement écrits : open/write_text/ json.dump). Résultat : 0 contradiction réelle. Les « écarts » remontés (docstrings sans liste-de-sorties en puces) étaient des faux positifs : commissions_gen.py documente bien ses sorties dans le bloc Sous-commandes (build → écrit commission_plan.json + MANIFEST.json), simple placement différent de financement (7 sorties → liste séparée). Uniformiser serait de la pure churn (#5) — écarté.

  2. Seule action honnête + non-redondante trouvée : currency du canal stakeholder. Le daily_reports/2026-08-05.md (session 004112, matin) s'arrête au jalon 33 jobs et précède toute la série d'attestation teeth des suites de MODULE (BATCH 1→4, commits 034144051154, clôture 25/25) jouée plus tard le même jour. Le canal stakeholder (two-logging-channels) ne captait donc aucun des deux axes de « dents ».

Fait. Ajout d'une section « Mise à jour fin de journée » au rapport du jour, consignant la clôture des deux axes : (A) 8/8 gates statiques (9fe8bf1/031134) · (B) 25/25 suites de module (6+6+6+7, 98f3f56/d25da28/e38dd93/986d293). Chaque chiffre sourcé sur une structure commitée (ls ci/*.sh hors lib.sh = 8 · gate.needs = 25 · série de commits) — 0 saisi à la main (#6). Message stakeholder explicité : les tables de currency prouvent que la CI est verte ; les attestations teeth prouvent qu'elle mord (un gate/ suite toujours-vert = faux vert invisible aux tables).

Vérifications. ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP (seul fichier touché = daily_reports/2026-08-05.md + ce log ; aucun fichier de code gaté). 0 chiffre inventé (#6) · faux findings docstring écartés · 0 édition de production · 0 gate ajouté (#5) · aucune commande VPS (#8) · aucun git clean.

Session 061201 · Sweep REPO-WIDE de la classe « chemin fantôme en code-span backtick » (ungated, documentée récurrente) → 0 fantôme réel · 0 édition de production

Choix de tâche. ./run_ci.sh au démarrage : 33 PASS · 0 FAIL · 0 SKIP, arbre propre, roadmap intégralement livrée+gatée (Sprint 8 buffer). Plutôt qu'une n-ième attestation redondante (#5), j'ai d'abord voulu fermer un axe encore incomplet. Deux axes de câblage CI vérifiés d'abord et confirmés structurellement clos : (a) parité local↔CI — run_ci.sh dérive sa liste de gate.needs (aucune liste en dur), et check_ci_integrity.sh verrouille ci.yml comme source de vérité ; (b) gate orphelincheck_ci_integrity INV-B itère git ls-files 'ci/*.sh' et exige que chaque script soit lancé par un job du gate ou sourcé par un gate lancé → un ci/*.sh mort/décâblé casse la CI. Rien à ajouter (#5).

Axe réellement balayé (nouveau périmètre). La classe « chemin in-repo fantôme dans un code-span backtick » est ungated à dessein (les backticks sont neutralisés par check_docs — cf. backtick-path-escapes-check-docs ; « ne pas gater tous les backticks »). L'audit d'exactitude des fiches (fiche-accuracy-audit-closed) ne couvrait que les 13 03_agents/*/AGENT.md. Je l'ai étendu à TOUT le markdown suivi (~200 fichiers : READMEs de modules, daily_reports/, 04_roadmap/, ci/README.md, DIRECTIVE_*, GAP_ANALYSIS, logs) via un harnais qui extrait chaque token backtické contenant un / et le résout contre quatre ancres : dossier du doc, racine du dépôt, 05_deliverables_mvp/, et chaque sous-module 05_deliverables_mvp/*/.

Résultat : 0 fantôme réel. Les ~180 tokens remontés se ventilent en trois formes bénignes, chacune vérifiée sur échantillon :

  1. Runtime/VPS hors-dépôt (/static/, otoia/capabilities/*, www/, seo_autonome/, data_room/PXX/) — non réfutables depuis le dépôt (#8), déjà connues.
  2. Citations cross-module en prose — un README/rapport nomme le fichier d'un AUTRE module par son nom court. Vérifié : crm/commissions/README.md cite banclib/finance.py à l'intérieur d'un vrai lien Markdown dont la cible existe et EST validée par check_docs (le backtick n'est que le libellé) ; banclib/wflib/cflib résolvent tous dans le dossier de leur module propre.
  3. Narratif d'historique + non-chemins pédagogiques — les 05_activity_log/* antérieurs citent des fichiers par nom court (prose, pas navigation), et certains chemins n'existent pas EXPRÈS : ./inexistant-xyz-123.md / ./this/does/not/exist.md sont les liens cassés d'exemple prouvant que check_docs mord, et /tmp/suite_teeth_*.sh sont les harnais de mutation-test éphémères (/tmp read-only). Correct par conception.

Conclusion honnête. L'axe fantôme-backtick est propre repo-wide (extension du 13→~200 .md), les deux axes de câblage CI sont clos, les axes teeth (8/8 statiques · 25/25 suites) et free-text/chiffres étaient déjà saturés. Aucun défaut réel trouvé → 0 édition de production (churner un doc exact serait de la dérive #5) et 0 gate ajouté (gater les backticks est explicitement rejeté, et un gate lisant /opt/oto serait non-portable côté runner Gitea — cf. ci-gate-verification-method). Seule sortie : ce log + une note mémoire pour que la prochaine session ne rejoue pas ce sweep à froid ni ne re-flague ces formes.

Vérifications. ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP (seul fichier touché = ce log ; aucun fichier de code gaté). 0 chiffre inventé (#6) · faux positifs backtick écartés par résolution multi-ancre + inspection · 0 édition de production · 0 gate ajouté (#5) · aucune commande VPS (#8) · aucun git clean.


Session 20260805_064201 · FIX RÉEL — la fenêtre de sprint roadmap-globale de faisabilite/generator était fausse (S2 → S3)

Défaut trouvé (première vraie édition de production depuis plusieurs sessions de sondes « 0 édition »). Le registre de l'auditeur 4Big attribuait au générateur de faisabilité 4 volets une fenêtre de sprint roadmap-globale erronée :

  • 05_deliverables_mvp/qa/audit_4big/quality_spec.json:66"sprint": "S2", "source": "roadmap Sprint 2 · Faisabilité générateur 4 volets …".

Or la roadmap ne contient AUCUNE faisabilité au Sprint 2 (04_roadmap/…:35-39 = Console / CRM / RBAC). La « génération 4 volets <1h » est explicitement au Sprint 3 (…:43, livrable …:46 « P07 Aqua Terra faisabilité complète auto-générée »). La citation source était donc falsifiable et fausse (classe prose-facts-vs-numeric-drift · sous-classe citation mal-attribuée + roadmap-anchor-gate).

Cause racine — confusion de deux cadres de « sprint ».

  1. Cadre agent-interne (phases de construction par agent) : 03_agents/faisabilite/AGENT.md §130-133 définit S1 = template · S2 = générateur 4 volets · S3 = version tracking — le tout ancré à la roadmap Sprint 3 (L41-46). Ce cadre est repris tel quel dans les docs de module (generator/README.md H1 « Sprint 2 », TEMPLATE_…:216, GAP_ANALYSIS_SPRINT1.md:74-75, publiciste/README.md:26, daily_reports/*). Cohérent dans son cadre — laissé intact.
  2. Cadre roadmap-global (autorité de la recette) : le champ sprint de quality_spec.jsontoutes les autres entrées le sourcent « roadmap Sprint N ». C'est le seul endroit qui doit porter le sprint roadmap-global. Il avait recopié le label agent-interne « S2 ».

Conséquence corrigée. Le livrable-phare S3 « P07 faisabilité complète auto-générée » était prouvé sans le module qui la génère (faisabilite/generator), tandis que ce dernier était compté comme preuve du S2 (« 7 dashboards · CRM · RBAC ») — auquel il n'a aucun rapport. La preuve la plus directe du livrable-phare était mal-classée.

Correctif (édition coordonnée 2-spec + régénération byte-gatée).

  • quality_spec.json:66 : "sprint" S2 → S3, source recitée « roadmap Sprint 3 · Faisabilité générateur 4 volets → data_room/PXX » (aligné sur le style de son jumeau bancable, ligne 71, déjà S3).
  • acceptance_spec.json : faisabilite/generator retiré de evidence_modules S2, ajouté à S3 (["faisabilite/bancable", "faisabilite/generator"]) — sinon l'invariant 5 « partition exacte » casse (want lu dans quality_spechave déclaré). Édition des DEUX specs obligatoire et suffisante.
  • Régénération : audit_4big (report) puis acceptance (lit la fenêtre depuis quality_spec).

Vérifications. partition_ok=True · bijective=True · S2 = 6 modules (publiciste + 5 rbac, exact) · S3 = {bancable, generator} (exact). ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP. Score 4Big du générateur inchangé 100/100 (le champ sprint ne sert qu'au tri + méta du report, pas d'oracle de scoring vérifié dans q4lib/scoring.py).

Portée honnête / anti-churn. 5 fichiers touchés (2 specs + 3 artefacts régénérés) + ce log. Zéro doc de module éditée : les ~10 « S2 » du générateur ailleurs sont le cadre agent-interne (ancré Sprint 3), corrects — les churner serait de la dérive #5. daily_reports/* = historique, non réécrit. 0 gate ajouté (surface déjà gatée par invariant 5 · verify-uncovered-before-gating), 0 chiffre inventé (#6, tout dérivé du roadmap), 0 commande VPS (#8), aucun git clean.


Session 20260805_074202 · FIX de COUVERTURE — le livrable transverse demo/scenarios manquait des DEUX fiches co-porteuses

Classe : complétude ≠ exactitude (fiche-accuracy-audit-closed) — même classe que le FIX précédent (CRM omettait financement_bancaire). Méthode : diff 05_deliverables_mvp/*/ (modules à générateur + tests + out/) vs les tables de livrables des 13 fiches 03_agents/*/AGENT.md.

Défaut réel trouvé. Le module 05_deliverables_mvp/demo/scenarios/ — livrable bel et bien produit (générateur demo_scenario_gen.py · 39 tests · out/{run_sheet,MANIFEST} · README · déjà gaté par check_readme_claims sur son propre README + job CI demo-scenario-tests) — n'était recensé dans AUCUNE des 13 fiches agents (grep demo|scenario sur 03_agents/*/AGENT.md = 0 hit). C'est un livrable transverse sans agent propre : son README le cadre « Sprint 7 (CRM + Faisabilité) » et la roadmap L68 l'assigne explicitement à CRM + Faisabilité. Les DEUX co-porteurs l'omettaient.

Correctif — 2 fiches, une seule source gatée (anti-duplication #5).

  • Fiche CRM (03_agents/crm/AGENT.md) : nouvelle sous-section « Livrable transverse · Sprint 7 — scénarios démo », avec table 1-ligne auto-gatée (mirroir du précédent financement_bancaire). La ligne pointe …/demo/scenarios/README.md et finit | 39 | → captée par le row_re de check_readme_claims (label \w+/ = scenarios/, chemin résolu demo/scenariosplan.suites) → cellule Tests recomputée depuis regression_plan.json. Framée honnêtement : pas un livrable CRM propre mais un méta-générateur qui compose les livrables amont, co-porté, ne partage aucune source avec le trio ni le financement.
  • Fiche Faisabilité (03_agents/faisabilite/AGENT.md) : cross-référence en prose (pas de chiffre redupliqué · #5) vers la fiche CRM + le README du module, mentionnant que bancable alimente le scénario S-P07-BANQUIER.

Auto-gates confirmés (3 axes indépendants, la ligne CRM franchit chacun).

  1. Fiche 03_agents/crm/AGENT.md · demo/scenarios — Tests 39 == source (39) (recompute regression_plan.json).
  2. Entrée CLI · demo_scenario_gen.py — verbes build|validate == subparsers.
  3. Traçabilité roadmap · demo/scenarios — citation l.68 == roadmap. Job CI demo-scenario-tests == ci.yml. check_docs : cross-refs ../crm/AGENT.md résolus OK.

Portée honnête / anti-churn. 2 fichiers touchés (2 fiches) + ce log. 0 gate ajouté (surface déjà couverte par le row_re générique des cellules Tests · verify-uncovered-before-gating · doc-numeric-claims-gate) — la nouvelle ligne hérite du gate existant. 0 chiffre inventé (#6 · 39 dérivé de la source unique). Le trio CRM + « Total CRM 81 tests » + le financement laissés INTACTS (demo hors-trio, source distincte). 0 production éditée (#5), 0 commande VPS (#8), aucun git clean. ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP.


Session 20260805_084202 · FIX RÉEL — « 6 sections » périmé dans les 2 docs d'implémentation du module financement_bancaire (le module en livre 7)

Classe : prose FACT count périmée, ungated (prose-facts-vs-numeric-drift · doc-numeric-claims-gate) — distincte des comptes gatés.

Défaut réel trouvé. Deux surfaces d'implémentation décrivant le module livré crm/financement_bancaire affirmaient « 6 sections » alors que le module en livre 7 :

  • 05_deliverables_mvp/crm/financement_bancaire/README.md:9 — « la structure des 6 sections »
  • 03_agents/crm/AGENT.md:47 — « cœur du gate + 6 sections + bannière »

Preuve du bon compte = 7 (triple-sourcé, byte-gaté) :

  • out/MANIFEST.jsoncounts.sections = 7 (artefact byte-gaté par check_regression/check_artifacts).
  • financement_spec.json → bloc sections = 7 (apport_initial, info_achat, choix_banque, formulaires, exigences, autorisations, envoi).
  • Le README lui-même se contredisait : ligne 49 énumère les 7 sections de l'accordéon, ligne 57 « 7 sections » (annotée « source de vérité = MANIFEST.json »). Seule la ligne 9 (prose libre) était restée à « 6 ».

Origine du « 6 ». La DIRECTIVE_FINANCEMENT_BANCAIRE_COMPLET originale (L106) énumère exactement 6 sections non-gating (Info achat / Choix banque / Formulaires / Exigences / Autorisations / Envoi), puis a été amendée pour ajouter la section gating apport_initial en position 1 → 7 au total. La spec + le module ont suivi (7), mais les 2 phrases de prose d'implémentation avaient gardé le « 6 » d'avant l'amendement.

Correctif — 2 surfaces d'implémentation, 6→7. Directives laissées INTACTES. Les 4 occurrences « 6 sections » restantes vivent dans des snapshots directive gelés (DIRECTIVE_FINANCEMENT… L106/L122/L131 + DIRECTIVE_WORKFLOW_FAISABILITE_V10 L22/L47) — input-specs datés de Michel, jamais réécrits (directive-vs-implementation · spec = authority, directive = snapshot). Leur « 6 » est historiquement exact au cadre pré-amendement (énumération des 6 aval). Seules les 2 surfaces décrivant le livrable sont corrigées.

Aucun gate ajouté (#5). Un gate structuré sur la ligne « Comptes courants » recomputerait la ligne 57 (déjà correcte, adossée à MANIFEST) mais ne mordrait pas la prose libre de la ligne 9 — la classe réelle du défaut. Financement reste le seul module dont MANIFEST.counts n'est pas recompté dans check_readme_claims ; SIGNAL surfacé (couverture prose ungated), pas d'ajout ce tour pour éviter un gate fragile + hors-cible. 0 chiffre inventé (#6 · 7 dérivé de MANIFEST/spec), 0 production éditée (#5), 0 commande VPS (#8), aucun git clean. ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP.


Session 20260805_101214 · FIX RÉEL — la fiche MOBILE présentait oto_module_mobile_api.py comme source alors que seul le .pyc existe (annotation « (compilé pyc) » perdue vs sa propre source citée)

Classe : inventory-vs-filesystem-drift — un claim d'existence d'asset dérive du filesystem VPS réel ; action sanctionnée = annoter pyc-only selon la convention du doc lui-même (distincte des claims chat.py/VPS-API qu'on SURFACE sans réécrire → registre D-03/D-04).

Défaut réel trouvé. La table « Modules OTOV7 réels refactorés » de 03_agents/mobile/AGENT.md:76 cite explicitement sa source AGENTS_EXISTING_ASSETS.md §8, mais la 1re ligne présentait /opt/oto/oto_module_mobile_api.py comme un module source sans qualificatif, alors que :

  • Sa source citée l'annote (compilé pyc) : AGENTS_EXISTING_ASSETS.md:59- /opt/oto/oto_module_mobile_api.py (compilé pyc). La fiche avait perdu l'annotation.
  • Le filesystem confirme : aucun .py à ce chemin ; seul /opt/oto/__pycache__/oto_module_mobile_api.cpython-310.pyc (13 Ko, root, 2026-07-02) existe. Classé PYC-only (vs les 4 autres lignes de la table — oto_module_mobile_download.py, deploy_mobile_rbac.sh, seed_mobile_rbac.py, oto_mobile.js — toutes SRC présentes, correctement laissées sans annotation).
  • La convention sœur confirme le format : AGENTS_EXISTING_ASSETS.md §9 annote de la même façon - /opt/oto/oto_module_plans_ifc.py (compilé pyc).

Correctif — 1 ligne, 1 fichier. Ajout de l'annotation *(compilé pyc)* à la seule ligne concernée, alignant la fiche sur (a) sa source citée §8, (b) le filesystem réel, (c) la convention §9. Les 4 autres lignes (sources réelles) laissées INTACTES — pas de sur-annotation.

Portée honnête / anti-churn. 1 fichier touché (1 fiche) + ce log. 0 gate ajouté (#5) — la couverture de ce claim d'existence porte sur un chemin VPS hors-repo non vérifiable par un gate dépôt (#8) ; l'annotation aligne la prose sur la source in-repo AGENTS_EXISTING_ASSETS.md, elle-même correcte. 0 chiffre inventé (#6). 0 production éditée (#5), 0 commande VPS (#8), aucun git clean. ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP.

Audits négatifs consignés (deux passes Explore + vérifs manuelles, RAS). Comptes/tests : les 15+ « N tests/sections/invariants/rôles » restent synchronisés (ex. ran=624 recomputé = 650 méthodes 26 qa.regression self-test = 624 ✓ · 24 suites matrice = 25 module-suites gate.needs 1 self-test ✓). Docstrings-vs-code : les 25 générateurs déclarent exactement leurs sorties/verbes CLI. Citations verbatim & « gaté par X » : échantillon roadmap L29/L38/L46/ L51/L63/L73 vérifié exact. Seul défaut concret = l'annotation pyc ci-dessus.


Session 121234 · RÉ-ATTESTATION indépendante du canal stakeholder (daily_report launch-readiness) → CLEAN · 0 édition de production

Contexte + choix de tâche. ./run_ci.sh au démarrage : 33 PASS · 0 FAIL · 0 SKIP, arbre propre. Roadmap ROADMAP_8_WEEKS_OR_LESS.md intégralement livrée+gatée in-repo ; les restes S3/S5/S6 (refactor BIM aec.py, builds EAS mobile, dépôts ONAPI, déploiement prod VPS) sont hors périmètre worker (CLAUDE.md #8 · VPS 153.75.250.214). Le daily_report 2026-08-05.md (sessions 004112+054200) désigne comme prochaine tâche « maintenir la currency des deux canaux + veille d'intégrité sur surfaces neuves » (two-logging-channels). Choix : re-attester par recompute python3 indépendant que chaque chiffre publié dans la table launch-readiness recharge à l'identique depuis les artefacts commités (pas un écho de la table) — les classes de dérive prose/count/link usuelles étant saturées et gatées (#5, ne pas re-signaler).

Recompute indépendant — 5 dimensions, toutes concordantes (aucune saisie manuelle · #6) :

Dimension Publié (daily_report) Recompute ce jour = ?
Qualité 4Big 24/24 modules à 100/100 · coverage.ok=true · ci==registry==24 · PASS quality_report.json : 24 modules, tous == 100, coverage.ok=true, ci=registry=24, verdict PASS
Régression (run) 24 suites · 624 ran · 624 passés · 0/0/0 regression_run.json.totals : suites 24 · ran 624 · passed 624 · failures/errors/skipped 0/0/0 · green 24 red 0
Recette roadmap 15/15 status=in_repo · 36 ci_job → 0 orphelin acceptance_matrix.json : matrix len 15 ; 36 refs ci_job (24 uniques) · 0 orphelin vs les 33 vrais jobs run_ci --list
Structure gate 33 jobs = 8 statiques + 25 suites run_ci.sh --list : 33 jobs (footer dérivé) = 8 gates ci/*.sh (hors lib.sh) + 25 suites module
Miroir local run_ci.sh → 33 PASS · 0 FAIL · 0 SKIP idem, inchangé

Contrôle nouveau (léger renfort, non publié auparavant). Vérification bidirectionnelle du non-orphelinage des ci_job de la recette : les 36 références (24 uniques, niché dans matrix[].modules[]) mappent toutes vers un job réel de gate.needs (0 orphelin dans les deux sens). Recoupé au décompte du gate lui-même (ls ci/*.sh hors lib.sh = 8 ; run_ci --list = 33). Aucun gate ajoutécheck_ci_integrity couvre déjà l'intégrité gate↔artefacts (#5, verify-uncovered-before-gating).

Portée honnête / anti-churn. 0 fichier de production édité (#5), 0 gate ajouté (#5), 0 chiffre inventé/saisi (#6 — tout dérivé de json.load/ls/--list), 0 commande VPS (#8), aucun git clean (#1). Le daily_report est exact et courant → laissé intact (l'éditer serait du churn). Seule édition : ce log. ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP (inchangé).


Session 20260805_124241 — FIX test-precision : constante 18 périmée + commentaire faux dans test_audit_4big.py

Surface NEUVE (hors classes saturées). Chasse ciblée aux surfaces non encore auditées (le canal usuel README/counts/docstrings/fiches est saturé, #5). Trouvaille réelle, pas un faux-positif : 05_deliverables_mvp/qa/audit_4big/tests/test_audit_4big.py test_failing_module_forces_global_fail figeait bad["totals"]["pass"] = 18 - fail.

Le défaut. Le périmètre audité a grandi à 24 modules (totals.modules == 24, tous 100/100), mais le test gardait l'entier 18 codé en dur. Résultat : le rapport « bad » qu'il fabrique avait pass=17, fail=1, modules=24 → il déclenchait INV7 (visé) MAIS AUSSI INV8 pass+fail ≠ modules (18≠24) et INV8 min/max score incohérents (module passé sous 95 alors que min_score restait 100). Or son propre commentaire affirmait « Le rapport est cohérent en interne mais porte un FAIL → INV7 le signale » — affirmation fausse : le rapport n'était PAS cohérent en interne. Le test passait quand même car son assertion (any("< seuil 4Big")) restait vraie parmi les erreurs parasites → test relâché, passant pour de mauvaises raisons, avec une constante latente qui pourrit en silence.

Preuve du diagnostic (recompute indépendant). Rejeu de l'ancien setup 18check_invariants renvoie exactement [INV7 publiciste …, INV8 pass+fail ≠ modules, INV8 min/max score incohérents]. La nouvelle assertion all("INV7" in e) échouerait sur ce setup → elle a de vraies dents (pas tautologique).

Le fix (test uniquement, 0 production). Totaux dérivés du vrai bad["modules"] au lieu d'un entier figé :

  • pass = len(modules) - fail (robuste à tout futur changement de décompte, #6 — aucun chiffre saisi à la main)
  • min_score/max_score = min/max(scores recomputés)

Le rapport « bad » devient réellement cohérent en interne → INV8/INV9 ne mordent plus, seul INV7 signale le module sous seuil — exactement ce que le commentaire prétend. Assertion resserrée de any("< seuil 4Big") (relâchée) vers errs non vide + all("INV7") + any("< seuil 4Big") : prouve désormais l'isolation d'INV7 comme unique déclencheur.

Portée / anti-churn. 0 fichier de production édité (seul un fichier de test), 0 artefact reconstruit (aucun out/*.json touché → pas de rebuild quality_report, cf. audit4big-rebuild-after-doc-edits), 0 gate ajouté (#5), 0 chiffre inventé (#6 — tout dérivé de len()/min/max), 0 commande VPS (#8), aucun git clean (#1).

Vérif. python3 -m unittest discover -s tests34 tests OK ; ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP. Distinct de la classe toothless-test/inert-mutation (module-suite-teeth-mutation) : ici la LOGIQUE testée est saine — c'est le HARNAIS de test (données figées) qui avait dérivé.


Session 20260805_131244 · FIX régression gate constraints-guard (tree committé RED malgré « 33 PASS » annoncé)

Constat. Au démarrage, ./run_ci.sh sur le tree committé (HEAD 127ef6f) → 32 PASS · 1 FAIL : job constraints-guard ROUGE. Le commit 127ef6f annonçait pourtant « 33 PASS CI » — il a vérifié les gates AVANT d'avoir son propre journal committé (réflexe ci-gate-verification-method non tenu : re-jouer les gates sur le tree AVEC le log staged).

Cause racine — 3e RÉCURRENCE de la sous-classe aucun git clean (cf. guard-constraints-log-prose), mais NOUVELLE variante. Ligne 1206 : … **0 commande VPS** (#8), **aucun \git clean`** (#1).— le motgit cleansans marqueur de <!-- ci-allow --> prohibition (« aucun » n'est pas dans la regex PROHIBITION). Le journaliste précédent AVAIT bien posé un… mais sur la ligne 1207 (paragraphe hard-wrappé). Or scan_forbiddenfaitgrep -in` ligne par ligne : l'escape doit être SUR la ligne fautive, pas sur une voisine. Donc l'échappatoire ne couvrait rien.

FIX (journal only, 0 prod, 0 artefact). Escape déplacé/fusionné INLINE sur la ligne 1206 elle-même (… (#1). <!-- ci-allow : … l'escape doit être SUR la ligne fautive, scan ligne-par-ligne -->) et suppression de la ligne-comment orpheline 1207.

Vérif. bash ci/guard_constraints.sh → OK ; ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP. grep -inE 'git[[:space:]]+clean' filtré PROHIBITION+ci-allow → 0 hit résiduel sur tout le tree.

Portée / anti-churn. 0 fichier de production, 0 gate ajouté (#5), 0 chiffre inventé (#6), 0 commande VPS (#8). Seule édition : ce journal. Mémoire guard-constraints-log-prose enrichie : l'escape ci-allow/marqueur est PAR LIGNE — un paragraphe wrappé qui sépare le terme de son marqueur laisse le gate mordre.


Session 20260805_134254 · SWEEP read-only docstring-vs-code drift sur 26 modules → CLEAN · 3ᵉ récurrence du faux-positif validate [-o OUT] refermée

Contexte + choix de tâche. ./run_ci.sh au démarrage : 33 PASS · 0 FAIL · 0 SKIP, arbre propre. Roadmap ROADMAP_8_WEEKS_OR_LESS.md intégralement livrée/gatée in-repo ; les restes S3/S5/S6 (refactor BIM, builds EAS, dépôts ONAPI, déploiement prod VPS) sont hors périmètre worker (CLAUDE.md #8). Le daily_report 2026-08-05.md et le OPEN_DECISIONS_REGISTER.md (D-01→D-05) sont déjà courants (sessions 004112 / 014119) — les ré-attester serait du churn (#5 ; déjà fait 3× ce jour, cf. 121234). J'ai donc visé la seule classe que la mémoire marque explicitement RECURRING / not-closed : docstring-vs-code-drift — docstrings/aides argparse de modules de PRODUCTION qui avancent un input/fallback/output/flag que le code contredit (ou qui omettent un output réellement écrit).

Méthode. Fan-out Explore en lecture seule sur les 26 modules générateurs (*_gen.py + lib/*.py + textes argparse embarqués), amorcé pour SKIP le faux-positif connu. Pour chaque module : docstring/help ↔ ce que le code lit (json.load/reads), écrit (open(...,'w')/write_text/ json.dump/noms de fichiers) et enregistre (add_parser/add_argument). Direction A (advertise non-implémenté) et B (omet un output réel) toutes deux couvertes.

Résultat — CLEAN. Aucune dérive réelle sur les 26 modules. L'unique item remonté par l'agent est exactement le faux-positif déjà refermé 2× (session 111224) : deploy_runbook_gen.py:28 validate [-o OUT] → (re)génère en mémoire, valide vs deploy.schema.json + invariants…. Vérifié en relisant la source (L26-29 + L288) : la ligne validate décrit ce que fait validate (régénère en mémoire, valide) et ne prétend jamais que -o écrit quoi que ce soit ; [-o OUT] n'est qu'un flag optionnel accepté, en symétrie CLI avec build, et argparse l'enregistre help="ignoré (compat)" (:288). Bijection parfaite, self-cohérente → NON-défaut. Seul build (:27) revendique l'écriture, ce que le code fait. Aucune édition de la docstring (#5 · la mémoire ordonne LEAVE/don't-harmonize : la doc est correcte, c'est le lecteur qui la sur-lit).

Portée / anti-churn. 0 fichier de production édité, 0 artefact reconstruit (aucun out/*.json touché → pas de rebuild quality_report, cf. audit4big-rebuild-after-doc-edits), 0 gate ajouté (#5), 0 chiffre inventé (#6), 0 commande VPS (#8 — sweep 100 % lecture), aucun git clean. Seule édition : ce journal.

Vérif. git status --short avant/après le sweep = vide (lecture seule) ; ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP. Mémoire docstring-vs-code-drift enrichie : classe re-balayée CLEAN sur les 26 modules ce jour, et le faux-positif validate [-o OUT] a récidivé une 3ᵉ fois (re-signalé par un Explore neuf malgré l'amorce de SKIP) → confirme qu'il faut le garder documenté comme piège, pas le "corriger".


Session 20260805_141301 · SWEEP neuf forward-compat warnings — 25 suites sous -W errorCLEAN (classe absente de la mémoire)

Contexte + choix de tâche. ./run_ci.sh au démarrage : 33 PASS · 0 FAIL · 0 SKIP, arbre propre (HEAD 48b19b9). Roadmap ROADMAP_8_WEEKS_OR_LESS.md intégralement livrée/gatée in-repo ; restes S3/S5/S6 (refactor BIM, builds EAS, dépôts ONAPI, déploiement prod) hors périmètre worker (CLAUDE.md #8). Les surfaces de dérive usuelles sont saturées et le daily_report 2026-08-05 + OPEN_DECISIONS_REGISTER sont courants — les ré-attester serait du churn (#5, déjà signalé ce jour, cf. 121234/134254). J'ai donc visé une classe de vérification que la mémoire ne couvre PAS : la compatibilité Python future du corpus de tests.

Hypothèse de risque. Le runner tourne aujourd'hui sous Python 3.10.12 ; toutes les suites sont VERTES. Mais un DeprecationWarning/SyntaxWarning/ResourceWarning émis silencieusement aujourd'hui (ex. escape-séquences d'expression régulière non-r"", datetime.utcnow(), sockets/ fichiers non fermés) reste invisible aux tables de currency : la CI est verte tant que le warning n'est pas promu en erreur — ce qui arrive tout seul à un futur bump d'interpréteur. Un vrai faux vert forward-looking, orthogonal aux axes teeth (qui prouvent que les gates mordent aujourd'hui).

Méthode (lecture seule). Rejeu des 25 suites de gate.needs (bijection 25 dossiers tests/ ↔ 25 jobs, déjà gatée) avec les warnings promus en erreurs : python3 -W error::DeprecationWarning -W error::SyntaxWarning -W error::PendingDeprecationWarning -W error::ResourceWarning -m unittest discover -s tests depuis le working-directory de chaque module. Le tests/ racine a aussi été balayé : il rend « Ran 0 tests » — attendu, c'est la suite Playwright e2e (smoke.spec.ts, TypeScript, job manuel workflow_dispatch e2e-baseline hors gate de merge car exige un serveur live), pas une suite python.

Résultat — CLEAN. 25/25 suites OK sous warnings-as-errors (624 tests au total, cohérent avec regression_run.json). 0 warning latent dans tout le corpus python : le corpus est prêt à un futur durcissement d'interpréteur sans régression silencieuse. Aucune suite ne dépend d'une API dépréciée ni n'émet de ResourceWarning (pas de handle de fichier/socket fuité dans les fixtures).

Portée / anti-churn. 0 fichier de production édité, 0 artefact reconstruit (aucun out/*.json touché → pas de rebuild quality_report, cf. audit4big-rebuild-after-doc-edits), 0 gate ajouté (#5 — attester ≠ multiplier les gates ; ce n'est pas une classe de dérive mais une propriété forward-compat, re-jouable à la demande via la commande ci-dessus après tout bump Python ou ajout de suite), 0 chiffre saisi à la main (#6 — 25 et 624 dérivés de gate.needs/du runner), 0 commande VPS (#8 — sweep 100 % local & lecture seule). Seule édition : ce journal + une entrée mémoire neuve.

Vérif. git status --short avant le sweep = vide (lecture seule) ; ./run_ci.sh après → 33 PASS · 0 FAIL · 0 SKIP (aucun fichier gaté touché). Nouvelle mémoire forward-compat warnings sweep consignée pour re-jeu futur.


Session 20260805_144304 — Currency canal stakeholder + consignation d'un incident d'intégrité

Constat. run_ci.sh33 PASS · 0 FAIL · 0 SKIP ; roadmap fonctionnelle intégralement livrée+gatée, restes hors périmètre worker (VPS/EAS/ONAPI · #8). Le canal stakeholder daily_reports/2026-08-05.md s'arrêtait à la session 054200. Sept commits ont suivi dans la journée. La ré-attestation de 9b6ec60 (121234) avait confirmé le canal courant MAIS n'éditait que ce journal, pas le fichier daily_report — qui laissait donc croire à une journée VERTE ininterrompue alors qu'un tree commité RED est survenu depuis.

Gap réel fermé. 127ef6f (124241) annonçait « 33 PASS » mais commitait un tree RED (32 PASS · 1 FAIL) — le gate guard_constraints/constraints-guard mordant sur le tree lui-même ; 1321983 (131244) a rattrapé la régression sous ~50 min (tree revenu VERT). Cet incident d'intégrité matériel pour le stakeholder était absent du canal daily_report. Ajouté : section fin-de-journée 144304 documentant (a) l'incident RED→VERT (preuve que la discipline de gate est opposable au tree commité, pas seulement en pré-commit), (b) les deux sweeps lecture-seule qui ont suivi (docstring-vs-code 48b19b9 CLEAN 26 modules · forward-compat warnings f311a1b CLEAN 25/25), (c) ré-attestation du point courant HEAD f311a1b.

Recompute indépendant (aucun chiffre saisi · #6). quality_report.json = 24 modules · tous == 100 · coverage.ok=true · verdict PASS · regression_run.json.totals = suites 24 · ran 624 · passed 624 · 0/0/0 · run_ci.sh = 33 PASS = 8 statiques + 25 suites. Toutes les tables du daily_report concordent ce jour.

Portée / anti-churn. 0 fichier de production édité, 0 artefact reconstruit (aucun out/*.json touché → pas de rebuild quality_report, cf. audit4big-rebuild-after-doc-edits), 0 gate ajouté (#5 — l'intégrité CI est déjà gatée par check_ci_integrity + guard_constraints), 0 chiffre saisi à la main (#6), aucune commande VPS (#8 — 100 % local). Gates re-joués sur l'arbre édité : check_docs (liens internes OK), guard_constraints . Éditions : daily_report 2026-08-05.md (section 144304) + ce journal. Classe two-logging-channels.

Vérif. ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP (aucun fichier gaté touché).


Session 20260805_171321 · Sweep docstring-vs-code étendu aux helpers *lib/ — CLEAN, 1 faux-positif neuf réfuté

Constat. run_ci.sh33 PASS · 0 FAIL · 0 SKIP. Roadmap fonctionnelle intégralement livrée+gatée ; restes hors périmètre worker (VPS/EAS/ONAPI · #8). Aucune tâche fonctionnelle in-repo résiduelle. Valeur honnête du jour : étendre la veille d'intégrité docstring-vs-code (classe RÉCURRENTE/non-close, cf. docstring-vs-code-drift) à la surface qu'un balayage scopé *_gen.py rate : les paquets d'aide *lib/.

Sweep exécuté (lecture-seule). Fan-out Explore scopé UNIQUEMENT aux helpers seolib · publiciste/lib · deploylib · dvlib · finlib · wflib · commlib · ecflib · genlib — docstrings module + fonction, directions A (annonce non-implémentée) ET B (omet une sortie réelle) → 0 dérive réelle.

Faux-positif neuf réfuté (vérifié en source + contre le spec). Le seul candidat remonté : crm/financement_bancaire/finlib/gate.py:58, docstring de apport_requis_usd « Apport requis = prix × taux (20 % résident RD, 30 % étranger) », soupçonnée de « coder en dur une constante que le code lit du spec ». NON — accurate-to-contract : la parenthèse illustre les valeurs COURANTES du spec, vérifiées égales à financement_spec.json (resident_rd=20 · etranger=30) ; elle reflète la ligne 8 de la MÊME docstring module (jugée correcte) ; les lignes 15-18 posent déjà le cadre « JAMAIS codés en dur, viennent du contrat ». Le code (l.59-64) lit bien cfg.taux_apport_pct[...]. → « docstring citant la valeur courante d'un nombre spec-driven en illustration » = même famille que l'exclusion pédagogique/illustrative. LAISSÉ (anti-churn #5) — pas d'harmonisation, pas de re-flag. Leçon consignée en mémoire : vérifier la valeur COURANTE du spec avant de qualifier de contradiction-hardcode.

Portée / anti-churn. 0 fichier de production édité, 0 artefact reconstruit (aucun out/*.json touché → pas de rebuild quality_report, cf. audit4big-rebuild-after-doc-edits), 0 gate ajouté (#5 — les docstrings sont de la prose, faible valeur à gater ; la parenthèse est de surcroît exacte), 0 chiffre saisi à la main (#6 — les 20/30 sont lus du spec, non retapés), aucune commande VPS (#8 — 100 % local). Éditions : mémoire docstring-vs-code-drift.md (note du faux-positif) + ce journal.

Vérif. ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP (aucun fichier gaté touché).


Session 20260805_174324 · GATE NEUF — prose #9 des modules bancable + commissions ancrée sur CLAUDE.md

Contexte. Repo vert au démarrage (33 PASS · 0 FAIL · 0 SKIP), tous modules livrés, roadmap 8-sem sans tâche-feature ouverte. Sonde ciblée sur la classe claude-md-constant-anchor-gate : les constantes canoniques #9 (3 % édition · 8.5 % marketing · 52 % point d'équilibre) recopiées hors du module generator.

Constat (mutation-prouvé). sed #9 8.5 %→9 % sur CLAUDE.md → le gate check_readme_claims.sh fait rougir 8 surfaces generator/template (model.py CANONICAL, README, renderer _CANONICAL_MARKERS, oracle test, 2 docstrings scorer, template intro/§5.2/§7) — l'ancre est vivante. MAIS 4 copies restaient périmées EN SILENCE, dans DEUX modules DISTINCTS, chacune se réclamant explicitement « (#9) » / « CLAUDE.md #9/#10 » :

  • (a) faisabilite/bancable/README.md — « 3 % édition » et « 8.5 % marketing » (#9)
  • (b) faisabilite/bancable/banclib/finance.py — MÊME phrase, en docstring
  • (c) faisabilite/bancable/banclib/deps.py — « CLAUDE.md #9/#10 (3 %/8.5 %/52 % · USD+DOP · Cardnet · Letter US) » (6 marqueurs)
  • (d) crm/commissions/README.md — « pourcentages canoniques (3 % édition · 8.5 % marketing · 52 % point d'équilibre) »

Corriger le generator ne TOUCHE pas ces modules → invention #6 latente, de la classe déjà fermée 10× (publiciste #10 · fiscal moneda · fiches #10 · template canonique).

Fix. Extension du bloc d'ancrage existant dans ci/check_readme_claims.sh (juste après « Template canonique »), réutilisant exp déjà recomputé des lignes #9/#10 de CLAUDE.md (zéro re-parse, zéro duplication). Un claim absent échoue AUSSI (traçabilité #6). Documenté en ci/README.md §2 (paragraphe « paramètres canoniques »), pas de nouvelle ligne au tableau §1 (cf. ci-readme-table-detail-in-section2).

Teeth (mutation-vérifié, arbre restauré à chaque fois).

  • #9 8.5 %→9 % → mord les 4 surfaces.
  • #9 52 %→55 % → mord (c)+(d) SEULEMENT ; (a)/(b) ne citent qu'édition+marketing ⇒ correctement épargnées = ciblage précis (pas de faux-rouge).
  • #10 Cardnet→Azul → mord l'énumération 6-marqueurs de (c).
  • Arbre restauré → gate exit=0, les 4 surfaces re-vertes.

Portée / anti-churn. 0 fichier de production édité (seuls ci/check_readme_claims.sh

  • ci/README.md), 0 artefact reconstruit (aucun out/*.json ni module doc scoré par audit_4big → pas de rebuild quality_report), 0 chiffre saisi à la main (#6 — les 6 valeurs sont recomputées de CLAUDE.md, jamais retapées), aucune commande VPS (#8 — 100 % local). Classe RÉCURRENTE (chaque module distinct qui recopie une constante #9/#10 citée est une surface à ancrer) : nouvelle sous-surface (bancable + commissions) close.

Vérif. ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP.