Files
oto-enterprise-os-dtp/05_activity_log/2026-08-05.md
T
Claude Code DTP Worker c196e4183e [DTP-Worker 20260805_234409] VÉRIF adversariale ciblée · candidat « MANIFEST.json manquant » de la fiche frontend_console = NON-défaut par design · 0 édition de production
Chasse à un vrai défaut ungaté (agent Explore skeptique) plutôt que répéter le motif de currency (churn). Candidat remonté puis RÉFUTÉ par vérif indépendante : la cellule « Sortie » de 03_agents/frontend_console/AGENT.md omet MANIFEST.json dans les DEUX lignes (portails:28 `out/workspace.json` alors que workspaces_gen.py:219 l'écrit ; chat_otoia:29 `{custom_block,chat_mount}` alors que chat_otoia_gen.py:229 l'écrit) → convention éditoriale (payload listé, manifeste de traçabilité omis), PAS une dérive. L'énumération complète des 3 fichiers est correcte là où elle compte (docstring chat_otoia_gen.py:12/15/18 + README module). Corriger la seule ligne chat = asymétrie ; éditer les deux = churn d'une cellule parsée par ci/check_readme_claims.sh (CO_FI ~L3506/L3576). CLEAN, laissé tel quel (#5/#6, « X not drift, don't fix »).

0 fichier de production édité, 0 artefact reconstruit, 0 gate ajouté (#5), 0 chiffre saisi (#6, 33 recompté de run_ci.sh), 0 VPS (#8). Seule édition in-repo : le journal. Mémoire projet : fiche-sortie-cell-omits-manifest-by-design.md. run_ci 33 PASS·0 FAIL·0 SKIP.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 23:50:09 +00:00

166 KiB
Raw Blame History

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

Session 20260805_234409 — VÉRIF adversariale ciblée : le candidat « MANIFEST.json manquant » de la fiche frontend_console est un NON-défaut par design (les 2 lignes l'omettent identiquement) · 0 édition de production

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 ; le canal de currency daily_reports/2026-08-05.md est déjà au HEAD effectif (8903a4b, porté par 21f55a9). Plutôt que répéter le motif récent de mise à jour de currency (churn), lancement d'une chasse adversariale à UN vrai défaut ungaté (agent Explore skeptique) sur les surfaces sous-balayées : prose-FACT vs artefacts · docstring↔code des *_gen.py/*lib/ · liens in-repo cassés.

Candidat remonté puis RÉFUTÉ. L'agent a signalé 03_agents/frontend_console/AGENT.md:29 (cellule « Sortie » du chat_otoia = out/{custom_block,chat_mount}.json, 2 fichiers) comme omettant le 3ᵉ fichier écrit par le code — chat_otoia_gen.py:227-229 écrit bien custom_block.json / chat_mount.json / MANIFEST.json. Vérification indépendante → NON-défaut : la ligne jumelle portails (AGENT.md:28, cellule = out/workspace.json) omet elle aussi MANIFEST.json, alors que workspaces_gen.py:218-219 l'écrit. Les deux lignes suivent donc la même convention éditoriale : la cellule « Sortie » liste la charge utile (payload), pas le manifeste de traçabilité. L'énumération complète des 3 fichiers est bien présente et correcte là où elle doit l'être — docstring du générateur (chat_otoia_gen.py:12/15/18) + README de module. « Corriger » la seule ligne chat créerait une asymétrie ; éditer les deux churnerait une cellule parsée par un gate (ci/check_readme_claims.sh, CO_FI ~L3506/L3576 en extrait les « TROIS attributs data-derived » + comptes tests/invariants). Décision : CLEAN, laisser tel quel (#5/#6, motif « X not drift, don't fix »).

Mémoire projet. Nouveau fichier fiche-sortie-cell-omits-manifest-by-design.md (+ index) pour qu'une session future ne re-remonte pas le faux positif.

Portée / anti-churn. 0 fichier de production édité, 0 artefact reconstruit, 0 gate ajouté (#5), 0 chiffre saisi à la main (#6 — 33 recompté de run_ci.sh), aucune commande VPS (#8). Seule édition in-repo : ce journal.

Vérif. ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP · git status propre après commit.


Session 224359 · FIX RÉEL — commentaire fantôme dans le gate ci/validate_json.sh (check de conformité $schema annoncé, jamais implémenté) · nouvelle sous-classe comment-vs-code sur les scripts de GATE eux-mêmes

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 24 modules du quality_report.json (audit_4big) sont tous à 100/PASS (aucun < 95, contrainte #5) → la frontière automatisable restante est soit un arbitrage produit (bloqué Michel, cf. open-decisions-register), soit du déploiement VPS (hors périmètre #8). Bascule sur une surface de dérive non ré-auditée : les commentaires internes des scripts de gate ci/*.sh eux-mêmes — les sweeps docstring-vs-code antérieurs étaient scopés aux générateurs de production + helpers *lib/ (cf. docstring-vs-code-drift), jamais aux gates.

Finding (réel, isolé). ci/validate_json.sh:27 portait un commentaire orphelin — # Si un $schema est déclaré, contrôle que 'draft' est reconnu (info seule). — placé dans la branche then du parse-loop, mais aucun code ne lit $schema ni ne contrôle le draft : la branche n'imprime que le . Le gate valide la bonne-formation SEULEMENT (parse strict) ; la conformité structurelle au .schema.json est l'oracle tiers OPTIONNEL jsonschema de la suite régression (cf. regression-baseline-17-skips-by-design). Le commentaire promettait donc un check inexistant — même classe honnêteté que #6 (rien ne doit affirmer un contrôle absent).

Vérification d'isolement. Sweep Explore des 8 scripts de gate + lib.sh (check_readme_claims.sh 5600+ l., check_docs, check_artifacts, check_regression, check_ci_integrity, check_mobile_workflow, guard_constraints, lib.sh) pour la même classe (commentaire affirmant un comportement que le code n'implémente pas) → 0 autre dérive ; tous les autres commentaires sont synchrones avec un code qui les honore. Dérive localisée au seul validate_json.sh.

Fix (2 volets, honnête · 0 invention). (1) Suppression de la ligne fantôme l.27 (la branche then fait exactement ce que le code montre — imprimer le ). (2) En-tête : ajout d'un bloc PÉRIMÈTRE explicite disant que ce gate contrôle la bonne-formation SEULEMENT et que la conformité $schema vit ailleurs (l'oracle jsonschema de check_regression.sh, 17 skips par design) — le commentaire retiré devient une affirmation POSITIVE et vraie du périmètre, ce qui prévient la réintroduction du fantôme par un futur lecteur.

Portée / anti-churn. 1 fichier de gate édité (commentaire seul, 0 changement de logique exécutable — même AST bash), 0 artefact reconstruit, 0 gate ajouté (#5 — c'est un FIX de dérive, pas un nouveau contrôle ; la classe est isolée, un gate serait inerte/redondant), 0 chiffre saisi à la main (#6), aucune commande VPS (#8). bash ci/validate_json.sh = OK · ./run_ci.sh = 33 PASS · 0 FAIL · 0 SKIP avant et après (le fix ne touche aucun comportement gaté). Seules éditions : ci/validate_json.sh + ce journal.


Session 191333 · Audit de COMPLÉTUDE fiches AGENT.md (25 modules ↔ 13 fiches) — CLOSE CLEAN · réconciliation compte 22→25 · 0 édition de production

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 (0 checkbox ouverte). Deux sondes préalables négatives : (a) Explore very-thorough de dérive prose/docstring sur tout 05_deliverables_mvp/ (classes récurrentes docstring-vs-code + prose-facts-vs-numeric) = 0 dérive ungatée ; (b) relecture du méta-gate ci/check_ci_integrity.sh (INV-A gate.needs == jobs non-manuels · INV-B tout ci/*.sh câblé/sourcé · INV-C PYTHONUTF8) = câblage exhaustif, le risque « gate orphelin » que j'envisageais est déjà fermé. Surfaces de dérive usuelles saturées (#5) → bascule sur un axe non ré-audité récemment : la complétude des fiches (distincte de l'exactitude, cf. fiche-accuracy-audit-closed).

Méthode. Énumération des répertoires-modules RÉELS (tests/ portant ≥1 test*.py) → 25 modules. Le MEMORY portait « 22 modules livrés » : écart réconcilié — le split de rbac en 4 sous-générateurs (apply_plan · fixtures_gen · roleprofile_gen · userperm_gen)

  • le rbac racine portent le total réel à 25 (pas 3 modules neufs non-fichés : simple granularité de comptage). Cross-check de chacun contre les 13 03_agents/*/AGENT.md.

Résultat — 25/25 dans ≥1 fiche. 24 modules figurent comme LIGNES de table de leur fiche propriétaire (colonnes Module | Sprint | Rôle | Entrée CLI | Job CI | Tests). Deux modules n'ont pas de ligne de table dans la fiche faisabilite — vérifié DÉLIBÉRÉ, pas un trou :

  • pie/manifest — documenté en prose (03_agents/faisabilite/AGENT.md §40-52) avec note explicite « les comptes exacts (groupes Master Data, règles de synchro, registre downstream) font foi dans le README du module — cet agent en est la source, pas la copie ». Une ligne de table à cellule Tests réintroduirait un compte recopié (#6, anti-invention) et duperait la source (#5). Laissé tel quel.
  • demo/scenarios — sa ligne source-unique (auto-gatée) vit délibérément dans la fiche crm (§« Livrable transverse · Sprint 7 »), non redupliquée côté faisabilité (#5). La fiche faisabilité l'y renvoie explicitement (§31-38).

Conclusion. Complétude fiches CLOSE (confirme fiche-accuracy-audit-closed, compteur mémoire réaligné 22→25 · exemptions prose/source-unique documentées pour couper le re-chase). Faux-positif « pie/manifest manque une ligne » réfuté à la source — même classe de FP que le memory répète (paths deliverables-relatifs, illustrations spec-driven : lus comme dérive, n'en sont pas). 0 fichier de production édité · 0 gate ajouté (#5) · 0 chiffre saisi à la main (#6, tous recomptés) · aucune commande VPS (#8) · aucune plateforme git externe. Deliverable de session = ce log + màj memory. ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP.

Session 184329 · CURRENCY canal stakeholder — baseline régression corrigée (607/17-skip) + 2ᵉ incident RED consigné · 0 édition de production

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 (0 checkbox ouverte ; 15/15 acceptance_matrix status=in_repo). Plutôt qu'une énième sonde de dérive (surfaces usuelles saturées · #5), j'ai vérifié la currency du canal stakeholder daily_reports/2026-08-05.md (hors merge-gate : check_docs+check_readme_claims l'excluent explicitement) contre les artefacts courants — dernière mise à jour du rapport = session 144304 (HEAD f311a1b), 5 commits plus tôt. Deux écarts matériels trouvés.

Écart 1 — baseline régression : faux-vert jsonschema neutralisé (matériel). Recompute indépendant python3 de regression_run.json.totals : 624 ran · 607 passed · 17 skipped · 0/0, verdict PASS — mais le rapport publiait « 624 passés · 0 skip ». Cause : le refactor 9d30939 lance chaque suite sous python -S (sans site-packages) reproduisant le runner Gitea pip-less → l'oracle optionnel jsonschema (17 tests skipUnless, ~1/module) est neutralisé partout. AVANT : jsonschema présent en local → skipped=0 ; absent sur runner → skipped=17regression_run.json commité = FAUX-VERT (vert local, ROUGE runner). APRÈS -S : matrice byte-déterministe cross-environnement, baseline ancrée sur la couverture garantie (stdlib + validateurs maison). Verdict inchangé PASS. La ligne « Régression » du canal se lit désormais 607 passés · 17 skip (oracles optionnels) ; les « 624 passés · 0 skip » antérieurs = snapshots datés vrais à l'époque (historical-repro = KEEP).

Écart 2 — 2ᵉ tree commité RED, capté le jour même. Le refactor -S (9d30939) éditait regression/README.md (+11 l) sans rebuild de quality_report.json (l'audit 4Big score le contenu doc → champ evidence byte-count) → check-artifacts ROUGE (32 PASS · 1 FAIL). Rattrapé sous ~17 min par 9fa464d (rebuild → 1 ligne, verdict PASS 24/24 inchangé). Même classe rebuild-after-doc-edits que l'incident du matin (127ef6f1321983). Le rapport n'en consignait qu'un ; ce 2ᵉ était absent → honnêteté du canal rétablie (cf. audit4big-rebuild-after-doc-edits, two-logging-channels).

Fix. Ajout d'une section « Mise à jour fin de journée · 184329 » au daily_reports/2026-08-05.md : (1) tableau avant/après -S + reformulation de la ligne Régression ; (2) tableau du 2ᵉ incident ; (3) veille lecture-seule depuis f311a1b (docstring helpers db91a6b CLEAN · gate constant-anchor 1441693 · invariant INV-C PYTHONUTF8 1a44dfepas un nouveau job, 33 inchangé) ; (4) ré-attestation HEAD 1a44dfe. Tables du matin laissées intactes (snapshots datés), réconciliées explicitement par la nouvelle section (même convention que l'évolution 32→33 jobs).

Ré-attestation (recompute python3 indépendant, HEAD 1a44dfe). 4Big : 24 modules · tous == 100 · coverage.ok=true · PASS. Régression : 624 ran · 607 passed · 17 skipped · 0 fail / 0 err · PASS. Recette : 15/15 in_repo · ci_job ×36 · True. Structure : run_ci 33 PASS = 8 gates statiques (ls ci/*.sh hors lib.sh = 8) + 25 suites.

Portée / anti-churn. 0 fichier de production édité, 0 artefact reconstruit, 0 gate ajouté/modifié (#5), 0 chiffre saisi à la main (#6 — 24/607/17/33/36 recomputés des artefacts + run_ci.sh), aucune commande VPS (#8 — 100 % local, lecture-seule sur les artefacts). Seules éditions : le rapport stakeholder + ce journal.

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

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.


Session 20260805_194334 · GATE NEUF — complétude de l'énumération des gates statiques dans la fiche QA

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 fiche-completeness (cf. fiche-accuracy-audit-closed — « completeness≠accuracy, diff deliverables vs fiche tables »), cette fois sur une fiche qui énumère un ensemble censé être exhaustif.

Constat (drift présent-temps réel). La fiche 03_agents/qa/AGENT.md (§« Deuxième étage QA · batterie de gates statiques ci/*.sh ») liste les gates dans un tableau, en se déclarant explicitement (l.49) « la liste ci-dessous est donc l'état courant, pas une constante ». Or le tableau ne comptait que 7 lignes alors que git ls-files 'ci/*.sh' (hors lib.sh) en rend 8 : le 8ᵉ gate check_mobile_workflow.sh — livré le jour même (session 001109, cf. rapport matinal 33-jobs) — n'y figurait NULLE PART (grep -c check_mobile_workflow fiche = 0, les 7 autres ≥ 1). La fiche promettait la complétude sans la tenir : omission silencieuse d'un jour entier. Surface ungatée confirmée : check_ci_integrity verrouille le câblage CI (ci/*.sh ↔ job gate.needs) mais ne lit pas la fiche ; check_readme_claims ne recomputait aucune énumération de gates → aucun gardien.

Fix (2 volets).

  1. Correction du fond : ajout de la ligne manquante check_mobile_workflow.sh au tableau, placée juste après check_ci_integrity.sh (les deux gardent l'intégrité d'un fichier-workflow) — rôle décrit fidèlement d'après l'en-tête du gate (second workflow mobile-build.yml, hors gate.needs, gating if: job-level sur needs.preflight.outputs.* jamais secrets., contrat d'outputs, MOB-1..4).
  2. Ancrage anti-récurrence : extension du gate existant ci/check_readme_claims.sh (bloc Python ajouté avant sys.exit, réutilise subprocess/bad/good déjà en scope · zéro nouveau gate #5 · zéro nouveau fichier) — re-dérive l'ensemble depuis git ls-files 'ci/*.sh' (hors lib.sh, #6) et exige que chaque basename soit cité (backtické) dans la fiche ; un gate absent échoue. Rend la promesse « l'état courant » opposable.

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

  • MUT1 — retrait de la ligne check_mobile_workflow.shRED nommant le gate réel non cité (n'énumère PAS ['check_mobile_workflow.sh'] … 8 gates réels).
  • MUT2 (forward-protection)git add d'un 9ᵉ ci/check_dummy9.sh factice (index seul) → RED (… ['check_dummy9.sh'] … 9 gates réels) : le prochain gate livré ne pourra pas être omis en silence. Fichier --cached retiré + rm (arbre byte-restauré, JAMAIS git clean · CLAUDE.md).
  • Arbre restauré → gate exit 0, les 8 gates re-verts.

Portée / anti-churn. 0 fichier de production édité (seuls la fiche QA, ci/check_readme_claims.sh, ci/README.md), 0 artefact reconstruit (aucun out/*.json ni doc scoré par audit_4big → pas de rebuild quality_report, cf. audit4big-rebuild-after-doc-edits), 0 chiffre saisi à la main (#6 — le « 8 » est recompté de git ls-files, jamais tapé), aucune commande VPS (#8 — 100 % local). Documenté en ci/README.md §2 sous check_readme_claims.sh (nouveau paragraphe « COMPLÉTUDE de l'énumération des gates statiques »), pas de nouvelle ligne au tableau §1 (cf. ci-readme-table-detail-in-section2).

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


Session 20260805_201339 · ATTESTATION chaîne d'intégrité des constantes mandataires — bout-en-bout · 0 édition de production

Contexte. Roadmap intégralement livrée+gatée ; baseline run_ci.sh verte (33 PASS). Aucune tâche fonctionnelle in-repo ne reste (cf. sessions du jour). Plutôt qu'ajouter un gate marginal (risque #5, cf. verify-uncovered-before-gating), la valeur honnête du jour : prouver de bout en bout que la chaîne de dérivation des constantes du mandat n'a AUCUN maillon hardcodé ni maillon ungaté, la classe de bug latent la plus coûteuse (un générateur qui fige 0.52/8.5 %/#f0b429 en littéral dérive en silence si CLAUDE.md change — précédent réel bancable_gen.py 0.52→finance._pct, cf. fix-vs-gate-transitively-protected-constant · claude-md-constant-anchor-gate).

Sonde 1 — aucun littéral hardcodé côté générateurs. Fan-out Explore sur 05_deliverables_mvp/** (recherche 0.52/0.085/0.03/3 %/8.5 %/52 %/#0a0a12/ #f0b429/Cardnet/USD/DOP/Fraunces/Cormorant en CODE exécutable des générateurs) : 0 littéral non-sourcé. Tous les générateurs lisent depuis 2 sources canoniques uniques :

  • faisabilite/generator/genlib/model.py → dict CANONICAL (6 valeurs : édition 3 %, marketing 8.5 %, équilibre 52 %, devises USD+DOP, Cardnet, Letter US) — consommé par bancable (finance._pct(deps.CANONICAL[...])), report, renderer.
  • publiciste/lib/branding.pyCOLOR_BG/COLOR_ACCENT/FONT_DISPLAY/FONT_BODY (#0a0a12 · #f0b429 · Fraunces · Cormorant Garamond) — importé par mobile/app_config, frontend/chat_otoia, seo (via deps.branding), publiciste/generator.

Sonde 2 — les 2 sources canoniques sont elles-mêmes ancrées à CLAUDE.md. Le maillon critique : une source unique n'est correcte que si ELLE est gatée contre le mandat. Vérifié dans ci/check_readme_claims.sh :

  • bloc l.5427 — model.py CANONICAL re-dérivé de CLAUDE.md #9/#10 (6 valeurs).
  • bloc l.6958 — branding.py CONSTANTES re-dérivées de CLAUDE.md #4 (2 couleurs + 2 fontes) + docstring + README:43 + oracle du test publiciste (+ renderer._CANONICAL_MARKERS). → boucle fermée : générateur → source canonique → CLAUDE.md, sans trou.

Sonde 3 — les 2 ancres-pivots MORDENT (mutation-prouvé, arbre byte-restauré).

  • MUT1 branding.py COLOR_ACCENT #f0b429→#e0a420RED : « Branding canoniques · branding.py CONSTANTES DÉRIVENT de CLAUDE.md #4 : accent='#e0a420' (attendu '#f0b429') ».
  • MUT2 model.py CANONICAL marketing_pct 8.5 %→9 %RED : « Faisabilité canoniques · model.py CANONICAL DÉRIVE de CLAUDE.md #9/#10 : marketing_pct='9 %' (attendu 8.5%) ».
  • Restauration ciblée par git checkout -- <path> (JAMAIS git clean, #interdits) → git status --porcelain vide · gate re-vert.

Faux-positif écarté (settled, NON édité). Le tableau « launch-readiness / Attestation » en tête du daily report 2026-08-05.md (l.30/45) lit « 624 passés · 0 skip » alors que regression_run.json.totals = 607 passés / 17 skip. Ce n'est pas une dérive neuve : la §CURRENCY du même fichier (l.146-171) reconcile explicitement (« les 624 passés · 0 skip des sections antérieures sont des snapshots datés », nouvelle baseline 607/17 posée par 1b99917) — classe dated-snapshot = KEEP déjà consignée regression-baseline-17-skips-by-design. Re-l'éditer = re-litiguer un settled → LAISSÉ.

Périmètre. 0 édition de production, 0 gate ajouté (#5 — chaîne déjà défendue end-to-end, sonde = attestation pas nouvelle machinerie), 0 artefact reconstruit, 0 chiffre saisi à la main (#6), aucune commande VPS (#8 — 100 % local). Classe mandate-constant-integrity CLOSE CLEAN ce jour (générateurs 0 hardcode · 2 sources · 2 ancres CLAUDE.md prouvées mordantes).

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


Session 20260805_204339 · CURRENCY canal stakeholder porté au HEAD + balayage lecture-seule multi-surface (0 édition de production)

Constat. Le rapport stakeholder 05_deliverables_mvp/daily_reports/2026-08-05.md attestait à HEAD 1a44dfe (session 181325, 18:13) et ne reflétait pas deux jalons plus tardifs du même jour : afa6165 (194334, gate neuf fiche-enumeration-completeness) et 27c7d20 (201339, attestation chaîne d'intégrité des constantes). Le canal roadmap (distinct du journal 05_activity_log/ per la discipline « deux canaux ») avait donc lapsé de 2 milestones. Précédent identique : 1b99917 (« CURRENCY canal stakeholder »).

Vérification amont (avant écriture, tout recomputé d'artefacts committés · #6).

  • Docstring↔code des 25 générateurs : sorties + comportement CLEAN (sous-classe filename déjà close 22/22 ; aucune nouvelle dérive input/fallback).
  • Comptes de tests par fiche AGENT.md vs def test_ réels : concordants — CRM 81 (trio 25+31+25) + financement 35 · ecf 39 · confotur 44 · seo 36 · portails 19 · chat 31. Tous déjà gatés.
  • Constantes CLAUDE.md #9/#10 (3 %/8.5 %/52 %/USD+DOP/Cardnet/Letter) : check_readme_claims.sh:5355+ les re-dérive de CLAUDE.mdgenlib/model.py + renderer markers + README ancrés. Aucune copie orpheline.
  • Runtime mobile : Expo 51 actuel / 54 cible cohérent sur toutes les surfaces « en place » (AGENT.md, AGENTS_EXISTING_ASSETS §8, roadmap L18/L56).
  • regression_run.json : 624 ran = 650 méthodes 26 (la suite qa-regression s'exclut de son propre méta-run) — self-consistant, byte-gaté par check_artifacts.
  • Fiche QA : cite désormais les 8/8 gates (git ls-files 'ci/*.sh' hors lib.sh = 8), verrouillé par le gate fiche-enumeration-completeness (afa6165).

Édition. Section « Mise à jour de currency · fin de journée (HEAD 27c7d20) » ajoutée au rapport stakeholder : tableau des 2 jalons (nature + opposabilité au merge) + état de vérification indépendant + portée anti-churn. Structure du gate inchangée : run_ci.sh33 PASS = 8 gates statiques + 25 suites (aucun jalon n'ajoute de job).

Portée / anti-invention. 0 fichier de production édité, 0 artefact reconstruit (le rapport n'est pas scoré par audit_4big — seuls les docs modules le sont), 0 gate ajouté (#5 — la journée était déjà entièrement gatée), 0 chiffre saisi à la main (#6 — 8/33/624 recomputés de git ls-files/run_ci.sh/l'artefact), aucune commande VPS (#8). Seules éditions : le rapport de currency + ce journal.

Vérif. ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP · git status propre après commit.


Session 20260805_211340 · GATE — complétude de l'énumération des gates dans la table §1 de ci/README.md (jumeau ungaté de la fiche QA, fermé afa6165)

Contexte + choix de tâche. Arbre propre au démarrage, ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP ; roadmap ROADMAP_8_WEEKS_OR_LESS.md intégralement livrée/gatée in-repo. Hypothèse dirigée par la mémoire prose-facts-vs-numeric-drift (5ᵉ sous-classe) : le 8ᵉ gate statique check_mobile_workflow.sh livré ce jour a pu laisser des siblings périmés dans les gate-docs ungatés. Sweep repo-wide \b7 (gates?|statiques?)\b / \b32 (jobs?|PASS)\b : 0 dérive réelle — toutes les occurrences « 7 »/« 32 » sont des snapshots datés historiques (daily_reports/activity_log 08-02→08-05) ou pédagogiques (le commentaire check_mobile_workflow.sh:13 « 7/7 gates statiques restaient verts » désigne les 7 gates pré-existants qui motivaient un 8ᵉ), classe correctement historical-repro/pedagogical = KEEP. Docs present-tense (ci/README §1, 8ᵉ gate statique) toutes cohérentes à 8/33.

Le finding réel — une surface UNGATÉE. La table §1 de ci/README.md (« Ce que fait le pipeline ») est LA doc canonique des jobs statiques : une ligne par gate ci/*.sh. Or sa complétude était le jumeau exact de la fiche QA fermée par afa6165 — mais non gaté : check_ci_integrity verrouille le CÂBLAGE (ci/*.shgate.needs), le bloc fiche-QA (ajouté afa6165) ne lit que 03_agents/qa/AGENT.md. Rien n'exigeait que chaque gate figure dans la table §1 → un 9ᵉ gate y serait omis EN SILENCE (même mode de défaillance que la fiche QA avait subi un jour entier). Prouvé ungate par mutation (verify-uncovered) : retrait de la ligne de table check_mobile_workflowrun_ci --static 8 PASS (aucun gate ne mord).

Fix — extension du gate existant (PAS un nouveau job · #5). Bloc Python ajouté à ci/check_readme_claims.sh (avant sys.exit), réutilisant _gate_names déjà re-dérivé de git ls-files 'ci/*.sh' (hors lib.sh · #6 — jamais une liste à la main). Il exige que chaque basename soit cité dans une ligne de TABLE (|…) de la section §1 — délibérément PAS la prose §1 (qui ne mentionne que certains gates pour l'explication mobile-build.yml : un contrôle whole-section serait édenté sur un retrait de ligne de table, comme la mutation l'a révélé — d'où le scope table-rows). 0 nouveau gate ci/*.sh, 33 jobs inchangés.

Teeth mutation-prouvés (2 axes). MUT1 — suppression de la vraie ligne de table check_mobile_workflow (l.24) → RED nommant check_mobile_workflow.sh (exit 1). MUT2 — 9ᵉ ci/*.sh factice tracké (index seul) → RED sur les DEUX surfaces (fiche QA + table §1 : forward-protection, le prochain gate ne pourra être omis d'aucune) ; git rm --cached + rm, arbre byte-restauré (JAMAIS git clean). Piège écarté : un premier essai sed 15d visait la ligne 15 = en-tête de table (le grep -n renumérotait relatif au pipe) — mutation inerte, détectée et re-ciblée sur la vraie ligne 24.

Doc. Sous-section « COMPLÉTUDE de l'énumération dans la TABLE §1 » ajoutée en §2 de ci/README.md (discipline ci-readme-table-detail-in-section2 : ne pas grossir la cellule §1).

Périmètre. 0 édition de production (générateur/out/), 0 artefact rebuild (aucune doc scorée audit_4big), 0 chiffre saisi à la main (#6 — 8/9 recomputés de git ls-files), aucune commande VPS (#8). Seules éditions : ci/check_readme_claims.sh (+40 l) et ci/README.md (+13 l).

Vérif. ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP · git status propre après commit.


Session 20260805_214344 · CURRENCY canal stakeholder porté au HEAD cb3de78 + audit lecture-seule (0 édition de production)

Contexte. Repo VERT à l'ouverture (./run_ci.sh → 33 PASS · 0 FAIL · 0 SKIP). Codebase très convergé, lourdement gaté. Balayage préalable pour un finding réel (pas un gate N+1 redondant · #5) avant de retomber sur la maintenance de currency.

Audit lecture-seule (0 dérive trouvée).

  • docstring↔code des 25 générateurs (sous-agent Explore, classe docstring-vs-code-drift marquée RÉCURRENTE) : chaque docstring/README annonce exactement les fichiers out/ écrits par le code ; complétude des fiches OK (21 sous-modules ↔ ≥1 03_agents/*/AGENT.md). CLEAN.
  • SEO roadmap L60 (Sprint 6 · trilingue FR/EN/ES · schema.org · hreflang) déjà livré : out/seo_keywords.json = 258 (fr=87 · en=87 · es=84, recomptés de l'artefact) + seo_schema_org.json + seo_hreflang.json ; le triplet est byte-gaté par check_readme_claims.sh (keywords_total+keywords_per_lang). Pas un gap.

Le finding réel — canal stakeholder stale d'un jalon. Le rapport roadmap 05_deliverables_mvp/daily_reports/2026-08-05.md attestait au HEAD 27c7d20 (session 201339) mais cb3de78 (session 211340, gate fiche-enumeration-completeness 2ᵉ locus = table §1 de ci/README.md) avait suivi et n'y était pas reflété. Discipline two-logging-channels (canal roadmap distinct du présent journal per-session) : porté au HEAD courant.

Édition. Section « Mise à jour de currency · fin de journée (HEAD cb3de78) » ajoutée au rapport : tableau du jalon cb3de78 (nature + opposabilité au merge) + état de vérification indépendant (complétude des gates verrouillée sur les deux loci — fiche QA afa6165 + table §1 cb3de78 — par le même check_readme_claims.sh) + portée. Structure du gate inchangée : 8 gates statiques (git ls-files 'ci/*.sh' hors lib.sh = 8, recompté) + 25 suites ; cb3de78 ne touche que l'infra CI (ci/README.md, ci/check_readme_claims.sh) + journal, aucun job.

Portée / anti-churn. 0 fichier de production édité, 0 artefact reconstruit, 0 gate ajouté (#5), 0 chiffre saisi à la main (#6 — 8/33/258 recomputés de git ls-files · run_ci.sh · l'artefact), aucune commande VPS (#8). Seules éditions : le rapport de currency

  • ce journal.

Vérif. ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP · git status propre après commit.


Session 20260805_221354 · SWEEP de vérification — bijection INVERSE des artefacts out/ (aucun fichier commité orphelin) · 0 édition de production

Contexte. Repo VERT à l'ouverture (./run_ci.sh → 33 PASS · 0 FAIL · 0 SKIP), très convergé. Balayage lecture-seule multi-surface préalable pour un finding réel avant tout travail (discipline anti-gate-N+1 · #5) :

  • CI : 8 gates statiques (git ls-files 'ci/*.sh' hors lib.sh = 8) + 25 suites = 33 jobs.
  • Complétude modules : 24/24 modules livrés (22 */*/tests + seo/tests + publiciste/tests) CI-câblés (gate.needs), régression-couverts (regression_plan.json = 24 suites, publiciste inclus), fiche-documentés (≥1 03_agents/*/AGENT.md). Aucun orphelin.
  • Dérive doc↔code (sous-agent Explore, 3 classes : docstring-vs-code · prose-vs-numeric · fiche-completeness) → CLEAN, 0 dérive présente-temps.
  • Registre décisions ouvertes (OPEN_DECISIONS_REGISTER.md) : les 5 items (D-01..D-05) re-vérifiés encore ouverts, chaque citation file:line encore exacte (D-01 pie_spec.json:24 contrats→legal/confotur + 5 downstreams null · D-02 gate.py:142 _cond_validation_wag + financement_spec.json:157 · D-03 chat.py absent fs / CLAUDE.md:28 le liste / footer L128 l'omet · D-04 projets_editor.py absent fs · D-05 bundle a_confirmer/null). Contenu à jour (tampon de session en retard, mais bumper sans changement de contenu = churn cosmétique — écarté).
  • publiciste : build/ .gitignore-d (artefacts reproductibles non commités · pattern bancable-out-not-committed), pas de sous-commande build ni de out/ commité → check_artifacts le saute correctement ; fiche L105 « aucun index.html commité » exacte. Pas un gap.
  • Sweep CWD-hétérogène écarté : check_artifacts (l.62) exécute chaque générateur depuis son propre répertoire module ; un sweep « CWD différent » produirait des faux positifs (les générateurs résolvent leurs entrées relativement au module par conception) — piège de faux-positif signalé à répétition en mémoire, abandonné.

Le finding — axe de robustesse génuinement NON couvert. check_artifacts.sh prouve la direction AVANT : pour chaque fichier produit par build ($tmp/*), un jumeau commité identique existe (l.71-93 : itère sur les fichiers produits). Il ne vérifie jamais l'INVERSE : qu'un fichier out/ commité est toujours produit par un build frais. Un fichier out/ orphelin — émis par un ancien générateur, plus produit — passerait silencieusement (aucune boucle sur les fichiers commités). Sibling non-déterminisme des sweeps hash-seed / locale-TZ / forward-compat déjà en mémoire, mais sur une propriété différente (complétude/orphelins des artefacts commités, pas déterminisme).

Sweep exécuté (lecture seule, rejouable). Pour chaque out/ (21 répertoires, tous avec un générateur build), build frais vers un mktemp -d, puis pour chaque fichier git ls-files du out/ commité : exiger sa présence dans le build frais. Résultat :

  • 58 fichiers out/ commités inspectés · 57 reproduits par build (bijection inverse OK).
  • 1 seul « orphelin » : qa/regression/out/regression_run.jsonBY DESIGN, artefact d'EXÉCUTION (run, pas build) explicitement exclu du contrat build par la docstring de check_artifacts (l.21-24) et byte-gaté séparément par check_regression.sh (régénère un run frais, exige l'identité byte-for-byte · mémoire regression-baseline-17-skips-by-design).
  • 0 orphelin réel. La bijection inverse tient ; confiance durable ajoutée.

Pourquoi PAS un gate (#5). La direction AVANT est déjà gatée (check_artifacts) ; l'INVERSE est empiriquement propre et un orphelin réel serait de toute façon transitivement attrapé (tout consommateur à build reproductible lisant un out/ périmé casserait sur check_artifacts). Ajouter un gate N+1 pour une propriété tenue = machinerie redondante. Consigné comme sweep rejouable, comme ses siblings déterministes — pas un ci/*.sh.

Portée / anti-churn. 0 fichier de production édité, 0 artefact reconstruit, 0 gate ajouté (#5), 0 chiffre saisi à la main (#6 — 8/24/33/58/57 recomputés en direct), aucune commande VPS (#8). Seule édition : ce journal (+ mémoire projet).

Vérif. ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP · git status propre après commit.


Session 20260805_231404 — CURRENCY canal stakeholder porté au HEAD + sweep docstring↔code CLEAN

Contexte. HEAD = 8903a4b. Le rapport roadmap stakeholder (daily_reports/2026-08-05.md, discipline « deux canaux ») s'arrêtait au jalon cb3de78 (session 211340) et ne reflétait pas 2 jalons plus tardifs du même jour.

Verif primaire — chasse aux défauts (classe docstring↔code, marquée RECURRING/non-fermée). Audit fan-out des docstrings de production vs comportement réel du code sur tous les *_gen.py

  • helpers *lib/ (parser/model/report/deps/builder/validator) : sorties écrites, flags argparse, comportements (anti-invention, déterminisme, invariants). Résultat CLEAN — aucune dérive haute-confiance. Non-défauts connus ré-écartés : {{placeholder}} (constraint #6), compat validate [-o OUT] self-consistant, chemins backtick deliverables-relatifs.

Verif secondaire — nature des 2 jalons portés :

  • d6a8d73 (221354) — SWEEP bijection INVERSE out/ (commité ⊆ produit) : 0 orphelin ; read-only, seul le journal touché. NON-gate (#5).
  • 8903a4b (224359) — FIX comment-vs-code dans ci/validate_json.sh : le commentaire évoquait un contrôle $schema/draft que le code ne fait pas (parse-only) ; aligné + renvoi à l'oracle jsonschema (17 skips par design). 0 logique de gate altérée.

Recompte structure de merge (aucun chiffre saisi · #6) : git ls-files 'ci/*.sh' hors lib.sh = 8 gates statiques (inchangé — aucun jalon n'ajoute de job) ; run_ci.sh = 33 = 8 + 25 suites.

Édition. Section « Mise à jour de currency · fin de journée (HEAD 8903a4b) » ajoutée au rapport : tableau des 2 jalons (nature + opposabilité au merge) + état de vérification indépendant

  • portée. Structure du gate INCHANGÉE.

Portée / anti-churn (session 231404). 0 fichier de production édité, 0 artefact reconstruit, 0 gate ajouté (#5), 0 chiffre saisi à la main (#6), aucune commande VPS (#8). Seule édition : ce journal + le rapport de currency.

Vérif. ./run_ci.sh33 PASS · 0 FAIL · 0 SKIP · git status propre après commit.