Le gate valide la bonne-formation JSON SEULEMENT (parse strict), mais l.27 portait un commentaire orphelin annonçant un contrôle de conformité $schema/draft que le code n'implemente jamais (branche then imprime juste le check). Nouvelle sous-classe comment-vs-code : les scripts de GATE eux-memes, jamais couverts par les sweeps docstring-vs-code (scopes aux generateurs de prod + helpers *lib/). Isolement verifie : sweep des 8 scripts ci/*.sh + lib.sh = 0 autre derive. Fix 2 volets : (1) suppression de la ligne fantome ; (2) en-tete PERIMETRE explicite (bonne-formation seule ; conformite $schema = oracle jsonschema optionnel de check_regression.sh, 17 skips par design) -> le commentaire retire devient une affirmation positive et vraie du perimetre. 0 changement de logique executable, 0 artefact rebuild, 0 gate ajoute (#5, FIX pas controle neuf), 0 chiffre saisi (#6), 0 VPS (#8). run_ci 33 PASS / 0 FAIL / 0 SKIP. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
161 KiB
Activity Log · 2026-08-05 · Claude Code DTP Worker
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
rbacracine portent le total réel à 25 (pas 3 modules neufs non-fichés : simple granularité de comptage). Cross-check de chacun contre les 1303_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 fichecrm(§« 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.sh → 33 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=17 ⇒
regression_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 (127ef6f→1321983). 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 1a44dfe — pas 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.sh → 33 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 C → C.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 FAIL — check-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.sh → 33 PASS · 0 FAIL · 0 SKIP.
Session 154314 · SWEEP neuf : indépendance LOCALE / TIMEZONE des artefacts build → 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). 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
- 1 ligne dans la table « Veille d'intégrité » du canal stakeholder + 1 mémoire neuve (locale-tz-determinism-sweep, sœur de hash-seed-determinism-sweep et forward-compat-warnings-sweep).
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.shaprè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.sh → 33 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) :
- 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é).
- 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). - 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 dansout/, et aucun fichier réel n'est omis d'une énumération se prétendant exhaustive. CLEAN 22/22 (bancableskip — 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 internement 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.sh → 33 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/main → int, 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_die → sys.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 /tmp → acceptance_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) classecrm/financement_bancairesous S4 · roadmap_line 52 (evidence_modulesde 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.mdliste 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_artifactsreste vert,git statusne 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 : deploy → gated_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 cp → str.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:114roles_couverts = sum(m["nb_roles"] …)+ 1→test_roles_restrictionrecompte le total vsmanifest.counts.roles_couverts. - audit-4big ·
q4lib/builder.py:30"fail": len(below)+ 1→test_real_build_passes_gateexigetotals.fail == 0. - demo-scenario ·
scenlib/builder.py:44duree = sum(b["duree_min"] …)+ 1→test_duree_est_somme_des_beatsrecomptesc["duree_min"]vs somme des beats. - regression ·
reglib/builder.py:44"test_methods": sum(s["test_methods"] …)- 1→test_totals_match_suitesrecompute la somme vstotals.test_methods. - deploy-runbook ·
deploylib/builder.py:59gated_set - mapped_set→ |→test_coverage_bijectiveexigebijective+missing==[]. - acceptance ·
acclib/builder.py:136"exact": want == have→ !=→test_partition_exact_all_sprintsexige chaquep["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_roles → BITE. 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.sh → 33 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_gen—fixturelib/builder.py:43if_owner = scope == SCOPE_IF_OWNER== → !=→ inverse tous lesif_owner,test_if_owner_reflete_scope_own(attend1 if scope=="own" else 0) mord.rbac/userperm_gen—permlib/builder.py:43by_mechanism[mechanism] += 1→ += 2→ total 50→100,test_manifeste_fidele(assertEqual(sum(by_mechanism.values()), 50)) mord.rbac/apply_plan—applylib/aggregator.py:174chaîneroles == cible == up_entries == rp_covered, 2ᵉ== → !=→couverture_bijectivebascule False,test_consistency_bijective(assertTrue) mord.mobile/app_config—mobilelib/builder.py:105roles = sorted(roles_map.get(key, []))+reverse=True→ ordre inversé,test_roles_allowed_egal_surface_rbac(==sorted(roles_map[portail])) mord.qa/audit_5d—qalib/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_otoia—chatlib/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.sh → 33 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/generator—scorer._score_completude20 * filled → 20 / filled(sed adressé L57 car la formule récurre L81) → le score4bigtombe < 95,test_complete_brief_scores_publiablemord (exit 1).faisabilite/bancable—finance.derivedpoint d'équilibremath.ceil(pe_brut) → math.floor→ 21→20,test_derived_arithmetic_traceablemord.crm/workflow_vente—buildertrans_rows.sort(...)+reverse=True→ casse le déterminisme trié,test_transitions_sorted_stablemord.crm/dossier_vente—is_submittableseuil>= "1"(voir ci-dessous) mord.crm/financement_bancaire—gate.apport_requis_usd/ 100.0 → * 100.0→ apport ×10 000,test_apport_requis_*mord.publiciste—_fmt_usdsuppression 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 desdoc_statusré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ègleis_submittable.publicistemin(prices) → maxest inerte car le retour de_prix_depuisn'alimente QUE la gateis 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.sh → 33 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 * taux → base + taux |
BITES exit=1 |
| 2 | pie-manifest |
pielib/builder.py : _statut inversé (gated↔a_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.sh → 33 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.sh → 33 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(contrats→legal/confotur) · siblingsnull: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ÉCISIONaudit-IA) ·finlib/gate.py:142-145(_cond_validation_wagteste encorewag_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.pyabsents). Toutes exactes. - D-05 (mobile) :
mobile_spec.json(a_confirmer) ·out/app_config.json(bundleIdentifier/packagenull) ·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 degate.needsest passé 32→33 il y a 2 jours (b7ffb64) ; le README refuse délibérément de le figer («gate.needsdé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: historique21→22→24(commentaires decheck_readme_claims.sh) ; ce chiffre est déjà gaté (recomputé depuisquality_report.jsontotals 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.needspassent · tous les modules 4Big au seuil100/100, verdictPASS» + 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.sh → 33 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:24attribue biencontrats(Promesa · Fideicomiso · HOA) àlegal/confotur— RÉEL. - D-03 :
CLAUDE.md:28listechat.py;/opt/oto/otoia/capabilities/chat.pyabsent — RÉEL. - D-04 :
AGENTS_EXISTING_ASSETS.md:127« projets_editor.py API en place » ; aucunprojets_editor.pysous/opt/oto/config— RÉEL (claim runtime VPS non réfutable, #8). Registre jugé fidèle → promotion légitime.
Éditions (2, README seul).
## Navigation— nouvelle ligne juste aprèsGAP_ANALYSIS_SPRINT1.md, pointant le registre (description : punch-list décisions produit ouvertes qui appartiennent à Michel · sourcée, ne tranche pas).## É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.sh → vert (chiffres README inchangés, fidèles).
ci/check_docs.sh → vert (nouveaux liens internes valides). ./run_ci.sh → 33 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
contrats→legal/confotur: REAL.pie/manifest/pie_spec.json:24attribuecontrats(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),contratsest 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) supersedewag_validated_byparaudit.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.pyabsent / D-04projets_editor.pyabsent : REAL, filesystem confirmé (ni.pyni.pycsous/opt/oto), déjà surfacés session20260803_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/packagequarantinésnull · 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.sh → 33 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.needs → run_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 234104 — secrets.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 unif:de job (^ if:). Le contextesecretsn'est pas exposé dansjobs.<id>.if(table d'availability GitHub Actions) → suract/Gitea il s'évaluerait vide → SKIP silencieux. C'est la régression234104verrouillée. - MOB-3 · activation différée (#6/#8) — chaque job de build (
run: eas build) est gaté surneeds.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'outputs —
preflightdéclarehas_token/has_repo, chaqueneeds.preflight.outputs.<X>référencé est déclaré (0 dangling) et chaque output déclaré est alimenté par unecho …>>$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.sh → 33 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 · verdictPASS.regression_run.json.totals: 24 suites · 624 ran · 624 passed · 0/0/0 · verdictPASS.acceptance_matrix.json: 15/15status=in_repo· verdictTrue·ci_job×36 (nichématrix[].modules[]).- Structure CI :
run_ci.sh --list= 33 jobs = 8 statiques + 25 suites ·ls ci/*.sh(horslib.sh) = 8.
Vérifications. ./run_ci.sh → 33 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 annoncerun_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— docstring50_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) etconfig/projets_editor.py(§13, « API GET/POST déjà en place » — siblingprojets_config.jsonprésent, lui). Vérification anti-invention #6 AVANT de flaguer :grep -rninterne → les deux MISS sont DÉJÀ surfacés à Michel par la session133718du 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.sh → 33 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) :
-
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.mdomis). 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.pydocumente bien ses sorties dans le bloc Sous-commandes (build → écrit commission_plan.json + MANIFEST.json), simple placement différent definancement(7 sorties → liste séparée). Uniformiser serait de la pure churn (#5) — écarté. -
Seule action honnête + non-redondante trouvée : currency du canal stakeholder. Le
daily_reports/2026-08-05.md(session004112, 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, commits034144→051154, 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.sh → 33 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 orphelin —
check_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 :
- 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. - Citations cross-module en prose — un README/rapport nomme le fichier d'un AUTRE module
par son nom court. Vérifié :
crm/commissions/README.mdcitebanclib/finance.pyà l'intérieur d'un vrai lien Markdown dont la cible existe et EST validée parcheck_docs(le backtick n'est que le libellé) ;banclib/wflib/cflibrésolvent tous dans le dossier de leur module propre. - 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.mdsont les liens cassés d'exemple prouvant quecheck_docsmord, et/tmp/suite_teeth_*.shsont les harnais de mutation-test éphémères (/tmpread-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.sh → 33 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 ».
- 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.mdH1 « Sprint 2 »,TEMPLATE_…:216,GAP_ANALYSIS_SPRINT1.md:74-75,publiciste/README.md:26,daily_reports/*). Cohérent dans son cadre — laissé intact. - Cadre roadmap-global (autorité de la recette) : le champ
sprintdequality_spec.json— toutes 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,sourcerecitée « roadmap Sprint 3 · Faisabilité générateur 4 volets → data_room/PXX » (aligné sur le style de son jumeaubancable, ligne 71, déjà S3).acceptance_spec.json:faisabilite/generatorretiré deevidence_modulesS2, ajouté à S3 (["faisabilite/bancable", "faisabilite/generator"]) — sinon l'invariant 5 « partition exacte » casse (wantlu dansquality_spec≠havedéclaré). Édition des DEUX specs obligatoire et suffisante.- Régénération :
audit_4big(report) puisacceptance(lit la fenêtre depuisquality_spec).
Vérifications. partition_ok=True · bijective=True · S2 = 6 modules (publiciste + 5 rbac,
exact) · S3 = {bancable, generator} (exact). ./run_ci.sh → 33 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édentfinancement_bancaire). La ligne pointe…/demo/scenarios/README.mdet finit| 39 |→ captée par lerow_redecheck_readme_claims(label\w+/=scenarios/, chemin résoludemo/scenarios∈plan.suites) → cellule Tests recomputée depuisregression_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 quebancablealimente le scénarioS-P07-BANQUIER.
Auto-gates confirmés (3 axes indépendants, la ligne CRM franchit chacun).
Fiche 03_agents/crm/AGENT.md · demo/scenarios — Tests 39 == source (39)(recomputeregression_plan.json).Entrée CLI · demo_scenario_gen.py — verbes build|validate == subparsers.Traçabilité roadmap · demo/scenarios — citation l.68 == roadmap. Job CIdemo-scenario-tests== ci.yml.check_docs: cross-refs../crm/AGENT.mdré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.sh → 33 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.json→counts.sections = 7(artefact byte-gaté parcheck_regression/check_artifacts).financement_spec.json→ blocsections= 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.sh → 33 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 §9annote 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.sh → 33 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.sh → 33 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 18 →
check_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 tests → 34 tests OK ; ./run_ci.sh → 33
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.sh → 33 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.sh →
33 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 error → CLEAN (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.sh → 33 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.sh → 33 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.sh → 33 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.sh → 33 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 (aucunout/*.jsonni module doc scoré par audit_4big → pas de rebuildquality_report), 0 chiffre saisi à la main (#6 — les 6 valeurs sont recomputées deCLAUDE.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.sh → 33 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).
- Correction du fond : ajout de la ligne manquante
check_mobile_workflow.shau tableau, placée juste aprèscheck_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 workflowmobile-build.yml, horsgate.needs, gatingif:job-level surneeds.preflight.outputs.*jamaissecrets., contrat d'outputs, MOB-1..4). - Ancrage anti-récurrence : extension du gate existant
ci/check_readme_claims.sh(bloc Python ajouté avantsys.exit, réutilisesubprocess/bad/gooddéjà en scope · zéro nouveau gate #5 · zéro nouveau fichier) — re-dérive l'ensemble depuisgit ls-files 'ci/*.sh'(horslib.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.sh→ RED nommant le gate réel non cité (n'énumère PAS ['check_mobile_workflow.sh'] … 8 gates réels). - MUT2 (forward-protection) —
git addd'un 9ᵉci/check_dummy9.shfactice (index seul) → RED (… ['check_dummy9.sh'] … 9 gates réels) : le prochain gate livré ne pourra pas être omis en silence. Fichier--cachedretiré +rm(arbre byte-restauré, JAMAISgit 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.sh → 33 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→ dictCANONICAL(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.py→COLOR_BG/COLOR_ACCENT/FONT_DISPLAY/FONT_BODY(#0a0a12 · #f0b429 · Fraunces · Cormorant Garamond) — importé par mobile/app_config, frontend/chat_otoia, seo (viadeps.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 CANONICALre-dérivé de CLAUDE.md #9/#10 (6 valeurs). - bloc l.6958 —
branding.pyCONSTANTES 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→#e0a420→ RED : « 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>(JAMAISgit clean, #interdits) →git status --porcelainvide · 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.sh → 33 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 — CRM81(trio 25+31+25) + financement35· ecf39· confotur44· seo36· portails19· chat31. 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 deCLAUDE.md→genlib/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 suiteqa-regressions'exclut de son propre méta-run) — self-consistant, byte-gaté parcheck_artifacts.- Fiche QA : cite désormais les 8/8 gates (
git ls-files 'ci/*.sh'horslib.sh= 8), verrouillé par le gatefiche-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.sh
→ 33 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.sh → 33 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.sh → 33 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/*.sh ↔ gate.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_workflow → run_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.sh → 33 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 ↔ ≥103_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é parcheck_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.sh → 33 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'horslib.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,publicisteinclus), fiche-documentés (≥103_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 citationfile:lineencore exacte (D-01pie_spec.json:24contrats→legal/confotur+ 5 downstreamsnull· D-02gate.py:142_cond_validation_wag+financement_spec.json:157· D-03chat.pyabsent fs /CLAUDE.md:28le liste / footer L128 l'omet · D-04projets_editor.pyabsent fs · D-05 bundlea_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 · patternbancable-out-not-committed), pas de sous-commandebuildni deout/commité →check_artifactsle saute correctement ; fiche L105 « aucunindex.htmlcommité » 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 parbuild(bijection inverse OK). - 1 seul « orphelin » :
qa/regression/out/regression_run.json— BY DESIGN, artefact d'EXÉCUTION (run, pasbuild) explicitement exclu du contratbuildpar la docstring decheck_artifacts(l.21-24) et byte-gaté séparément parcheck_regression.sh(régénère unrunfrais, exige l'identité byte-for-byte · mémoireregression-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.sh → 33 PASS · 0 FAIL · 0 SKIP · git status propre après commit.