Files
oto-enterprise-os-dtp/05_activity_log/2026-08-03.md
T
Claude Code DTP Worker dbde8fac46 [DTP-Worker 20260803_070711] Sprint 8 QA · fix · 3 surfaces jumelles résiduelles du mensonge regression_run.json « non déterministe/non commité »
Le fix 89eb8e6 (session 053701) avait corrigé regression_gen.py docstring +
README, mais la même contre-vérité subsistait dans 3 surfaces manquées :
 · reglib/builder.py:10-13 (docstring module — pointait vers .gitignore comme
   autorité alors que le .gitignore affirme l'INVERSE)
 · reglib/builder.py:71 (docstring run_matrix)
 · regression_gen.py:82-85 (champ hors_perimetre_worker → coule dans MANIFEST.json
   commité/gaté ; contredisait le docstring corrigé 12 lignes plus haut)

Ground-truth (4 sources) : git ls-files (commité) · .gitignore (commité+byte-
déterministe) · ci/check_regression.sh (exige byte-identité d'un run frais) ·
docstring regression_gen.py:26 déjà réaligné. MANIFEST.json régénéré (build),
plan + regression_run.json inchangés. Consommateur aval : aucun. run_ci.sh 30
PASS 0 FAIL. Classe prose-facts-vs-numeric-drift / docstring-vs-code, gate-neutre.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-03 07:15:25 +00:00

634 lines
44 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Activity Log · 2026-08-03 · Claude Code DTP Worker
## Session 063711 · Sprint 5/8 · Canal 2 `daily_reports/` remis à jour (3 sessions manquantes depuis 040643)
**Contexte + choix de tâche.** `./run_ci.sh` au démarrage : **30 PASS · 0 FAIL · 0 SKIP**,
arbre propre. Roadmap 1→8 intégralement livrée + gatée verte. Un sweep de dérive dédié
(agent Explore : globs de sortie README-vs-`os.path.join`, docstrings générateurs, comptes
prose, liens Markdown) a rendu **0 dérive ungated** — les gates existants
(`check_readme_claims`/`check_artifacts`/`check_regression`/`check_docs`) couvrent l'acquis,
et re-scanner serait redondant (#5). La valeur restante réelle in-périmètre : la **currency
du canal 2 stakeholder** (contrat « Rapports quotidiens »), classe [[two-logging-channels]].
**Défaut trouvé (dérive de couverture, réelle).** Le snapshot `daily_reports/2026-08-03.md`
s'arrêtait à l'addendum session `040643`. **Trois** sessions net postérieures manquaient au
snapshot — toutes des correctifs d'intégrité doc (aucun ne bouge les chiffres
launch-readiness, d'où table de clôture inchangée) :
- `050654``ci/README.md` complété (2e workflow `mobile-build.yml`, commit `1af5d01`) ;
- `053701` — fix prose-fact `regression_run.json` doc-vs-réalité (commits `89eb8e6` + `3d8ccb0`) ;
- `060704` — fix citation roadmap fantôme module bancable (commit `88d9955`).
**Réalisé.** Trois addenda concis insérés **avant** la table « fin de journée » (qui reste
la synthèse de clôture), 100 % sourcés : chaque addendum cite son/ses commit(s), sa classe
de dérive et le consommateur régénéré le cas échéant. Aucune valeur inventée.
**Chiffres re-dérivés d'artefacts (aucun saisi à la main).** `regression_run.json.totals`
= 22 suites · **569 ran / 569 passed · 0/0/0** ; `quality_report.json` verdict **PASS · 22
modules · min 100** (seuil 95) ; `acceptance_matrix.json` **15/15 `status=in_repo`**
(`all_modules_gated` + `all_artifacts_exist` all true) ; README Mobile **4931 octets**
(`wc -c`). Tous concordants → table de clôture laissée **inchangée** (correcte telle quelle).
**Sûreté du changement.** `daily_reports/` n'est ni un artefact `out/` byte-gaté ni une
source de claims de `check_readme_claims` (surfaces gatées = README/AGENT.md, pas les
rapports quotidiens) → édition **gate-neutre**. Aucun claim numérique gaté touché · aucun
terme interdit · aucune logique de prod ni de gate modifiée.
**Vérifications.** `./run_ci.sh`**30 PASS · 0 FAIL · 0 SKIP** (inchangé) · `git status`
= 1 fichier. **Zéro nouveau module · zéro gate ajouté (#5).** Aucune commande touchant au
VPS (#8) · aucun `git clean` <!-- ci-allow : mention de l'interdit absolu, jamais exécuté -->.
## Session 033642 · Sprint 5 P1 · Livrable FONCTIONNEL : workflow CI/CD Mobile `.gitea/workflows/mobile-build.yml` (build EAS iOS + Android)
**Contexte + choix de tâche.** `./run_ci.sh` au démarrage : **30 PASS · 0 FAIL · 0 SKIP**,
arbre propre. Michel : « commits fonctionnels, pas juste docs ». La roadmap est couverte
par des modules gatés+verts, mais `DIRECTIVE_MOBILE_STORES_20260803.md` (Sprint 5, actif)
liste explicitement en **P1 · CI/CD Mobile** la création de `.gitea/workflows/mobile-build.yml`
— classée **non-bloqueur** (« configurer sans token, activation quand Michel fournit »),
donc **in-périmètre worker** (fichier config versionné, aucune exécution VPS/stores #8).
Vérifié absent : `.gitea/workflows/` ne contenait que `ci.yml`. **Vraie tâche fonctionnelle
non faite** → exécutée.
**Livrable créé : `.gitea/workflows/mobile-build.yml`.**
- **Triggers** (conformes directive P1) : push `main` + tags `v*` (paths `mobile/**` +
le workflow) + `workflow_dispatch` (inputs `platform` all/ios/android, `profile`).
- **Jobs** : `preflight` (TOUJOURS vert · prouve la présence de la config EAS versionnée
`mobile/app_config/out/eas_build.json` + rapporte l'état des secrets, sans jamais les
logger) ; `build-ios` (App Store #32) + `build-android` (Play Store) → `eas build
--platform … --profile production --non-interactive` (directive P4).
- **Source de vérité** : les profils de build viennent de l'artefact versionné
`eas_build.json` (development/preview/production) — pas réinventés.
- **Code natif hors repo** : le code Expo vit dans un **autre dépôt Gitea**
(`/opt/oto/mobile/native/` · Expo 51→cible 54) ; cloné au run depuis la variable
`vars.MOBILE_NATIVE_REPO`**jamais recopié ici**.
**Sûreté (activation différée · #6 zéro invention · #8).** Les jobs de build sont gardés
par `if: secrets.EAS_TOKEN != '' && vars.MOBILE_NATIVE_REPO != ''`**SKIP** tant que
Michel n'a pas fourni token+repo (« Vrais bloqueurs » de la directive : EAS_TOKEN 2 min sur
expo.dev). Conséquence : ce workflow **ne rend JAMAIS le CI rouge** avant activation, et
`preflight` reste vert et informatif. Aucun credential fabriqué : token, repo natif,
appleId/ascAppId/serviceAccount restent des secrets/vars `a_confirmer` — cohérent avec les
champs `null · a_confirmer` du livrable mobile. Plateforme **Gitea Actions uniquement** (#2).
**Non-régression des gates (vérifié).** `check_ci_integrity.sh` n'inspecte QUE `ci.yml`
(chemin en dur `CI=".gitea/workflows/ci.yml"`) → le 2e workflow lui est invisible, aucun
job à ajouter à `gate.needs`. `guard_constraints.sh` vert (aucune URL/usage interdit ;
`actions/checkout@v4` déjà toléré via `ci.yml`). YAML valide (pyyaml).
**Consommateur régénéré (classe [[artifact-reproducibility-gate]]).** Ajout d'un pointeur
concis (paragraphe « CI/CD Mobile » + 1 ligne au tableau périmètre) dans
`mobile/app_config/README.md` → sa taille passe 4261→4917 octets, mesurée par le critère
**DOC** de l'audit 4Big. `check-artifacts` a viré ROUGE (dérive attendue) →
`audit_4big_gen.py build -o out` régénéré : **seule** la ligne `evidence "README.md (…octets)"`
change ; verdict inchangé **PASS · 22/22 modules ≥ 95**. Lien Markdown vérifié résolvant
(`../../../.gitea/workflows/mobile-build.yml`).
**Vérifications finales.** `./run_ci.sh`**30 PASS · 0 FAIL · 0 SKIP**. **Zéro nouveau
module · zéro gate ajouté (#5)** · aucune commande VPS (#8).
Aucun `git clean` exécuté <!-- ci-allow : mention de l'interdit absolu, jamais exécuté -->.
## Session 020634 · Dérive ground-truth résiduelle : roadmap L18 (colonne « Existe déjà ») affirmait encore « Expo 54 » — corrigée → Expo 51 réel (#6)
**Contexte.** `./run_ci.sh` au démarrage : **30 PASS · 0 FAIL · 0 SKIP**, arbre propre.
Focus mandat courant = Sprint 5 Mobile (`DIRECTIVE_MOBILE_STORES_20260803.md`). Le
module in-repo `mobile/app_config` est complet et vert (schéma + 12 invariants ·
comptes 5 onglets / 44 rôles / 3 langues / 13 `a_confirmer` vérifiés contre
`out/MANIFEST.json` · docstring/README cohérents). Les actions P1-P5 de la directive
visent l'app réelle `/opt/oto/mobile/native/` (**hors repo #8**, bloquées sur
EAS_TOKEN/credentials Michel). J'ai donc cherché une **dérive réelle in-repo** dans le
périmètre worker.
**Défaut RÉEL trouvé (prolonge la session 013634).** La session 013634 a corrigé la
fausse affirmation « Expo 54 **en place** » dans `AGENTS_EXISTING_ASSETS.md §8` + la
fiche `03_agents/mobile/AGENT.md`, en distinguant **runtime ACTUEL (51)** vs **cible de
rebuild S5 (54)**. Elle a **délibérément laissé la roadmap** — mais son raisonnement ne
visait que la **ligne 56** (Sprint 5 « **Rebuild Expo 54** » = la cible, décision P0 de
Michel, à ne pas trancher). Elle n'a **pas examiné la ligne 18** : la table « Gains
d'accélération identifiés », colonne « **Existe déjà** », qui disait
`modules RBAC + API + Expo 54`. Or « Existe déjà » = ce qui **existe déjà**
refactorer) : y écrire « Expo 54 » ré-affirme **exactement** la dérive « Expo 54 en
place » corrigée partout ailleurs — le runtime réel vérifié (`package.json`, session
013634 + directive audit 2026-08-03) est **Expo 51**, et 54 est la **cible de rebuild**
(colonne « Reste à faire » / Sprint 5 L56).
**Contradiction interne prouvée.** `03_agents/mobile/AGENT.md:88` **cite** cette ligne :
« État courant (**table roadmap L18**) : modules RBAC + API + **Expo 51** en place ». La
fiche affirmait donc que la L18 dit « Expo 51 » alors que la L18 disait « Expo 54 » — une
**citation en désaccord avec sa source**, introduite quand 013634 a corrigé la fiche sans
corriger la roadmap. Grep confirme : l'unique consommateur de la L18 est `AGENT.md:88` ;
le spec/MANIFEST mobile ancrent sur **l.56** (`roadmap_ref`/`expo_sdk_source`), jamais l.18.
**Correctif — chirurgical (1 ligne), fidèle, non-gaté vérifié AVANT édition.**
Roadmap L18 : `Existe déjà: … Expo 54``… Expo 51` (runtime réel), et
`Reste à faire: Builds + submit stores``Rebuild vers Expo 54 + builds + submit stores`
(rend la ligne honnête sur le travail restant, fidèle à Sprint 5 L56). Résout la
contradiction avec `AGENT.md:88` (qui devient exact). **Ligne 56 « Rebuild Expo 54 » NON
touchée** = la cible S5 / décision P0 de Michel, non tranchée.
**Sûreté (vérifiée AVANT édition).** Le gate Expo-SDK (`check_readme_claims.sh:7779-7794`,
ancrage roadmap INV-mobile) ancre via `re.search(r"Rebuild Expo (\d+)", roadmap)` → ne
lit **que** la ligne 56 ; le gate (d) ne scanne que `mobile/app_config/README.md`, jamais
la roadmap. La L18 était donc **ungated** pour ce « 54 ». Formulation « Rebuild **vers**
Expo 54 » choisie **exprès** pour ne PAS matcher le bigramme `Rebuild Expo` → l'ancre du
gate reste liée à la **L56** (pas de second littéral porteur, cf. `[[roadmap-anchor-gate]]`).
**Vérifications.** `./run_ci.sh`**30 PASS · 0 FAIL · 0 SKIP** (inchangé) ·
`grep 'Rebuild Expo' roadmap` = **1 seule occurrence, L56** (ancre intacte) ·
`git diff --stat` = 1 fichier, 1 insertion / 1 suppression. **Zéro nouveau module ·
zéro gate ajouté (#5) · aucune logique de prod ni de gate touchée** — pure correction de
dérive ground-truth sourcée, prolongeant [[mobile-runtime-actual-vs-rebuild-target]].
Aucune commande touchant au VPS (#8) · aucun `git clean` <!-- ci-allow : mention de l'interdit absolu, jamais exécuté -->.
## Session 013634 · Dérive ground-truth réelle : app mobile documentée « Expo 54 en place » alors que le runtime réel est **Expo 51** (upgrade 54 abandonné) — corrigée (#6)
**Contexte.** `./run_ci.sh` au démarrage : **30 PASS · 0 FAIL · 0 SKIP**, arbre propre.
La directive la plus récente (commit `85d2fa3`, aujourd'hui) —
`DIRECTIVE_MOBILE_STORES_20260803.md` — porte le Sprint 5 Mobile. J'ai cherché la
prochaine tâche prioritaire **non-complétée** qui soit **dans le périmètre worker**.
**Périmètre : l'essentiel de la directive Mobile est bloqué / hors-repo (#8).** Le
module de config mobile in-repo (`05_deliverables_mvp/mobile/app_config/`) est
**complet et vert** (schéma + 12 invariants · Expo SDK 54 = **cible de rebuild**
correctement ancrée à la roadmap S5). Les actions P1 (`mobile-build.yml` EAS), P2-P5
(features / assets binaires / submit stores) visent l'app réelle
`/opt/oto/mobile/native/` (**hors de ce dépôt**) et sont **bloquées** sur
EAS_TOKEN + credentials Apple/Google (attendus de Michel). Rien à construire ici.
**Défaut RÉEL trouvé (investigation read-only, aucune commande VPS).** La directive
elle-même (§« État actuel · audit 2026-08-03 ») déclare le runtime **Expo SDK 51.0.0**,
l'upgrade Expo 54 **abandonné le 2026-07-27**. Vérification indépendante du
`package.json` réel : `expo ~51.0.0` · `react 18.2.0` · `react-native 0.74.5` ·
`expo-router ~3.5.0` — et **même le backup `native.bak-upgrade54-*` est resté sur ~51**
(le bump 54 n'a jamais atterri). Or l'inventaire d'actifs
`AGENTS_EXISTING_ASSETS.md §8:63` affirmait « **Expo SDK 54 + React 19.1.0 + React
Native 0.81.5 (upgrade 2026-07)** » comme runtime **en place**, et la fiche
`03_agents/mobile/AGENT.md` recopiait ces versions (« Expo 54 en place »). C'est
**exactement la classe interdite** par CLAUDE.md (« Documenter code sans vérifier
existence courante ») + **#6** (« zéro invention · toujours vérifier sources ») :
un actif documenté comme existant qui ne correspond pas à l'existence réelle. Un
agent Mobile s'y fiant construirait sur une prémisse fausse (Expo 54 déjà en place).
**Correctif — chirurgical, sourcé, distinguant ACTUEL vs CIBLE-REBUILD.**
1. `AGENTS_EXISTING_ASSETS.md §8:63` : runtime **actuel** corrigé en Expo 51.0.0 /
React 18.2.0 / RN 0.74.5 / expo-router ~3.5.0 (vérifié `package.json`), avec note
« upgrade 54 abandonné 2026-07-27 · cible rebuild = 54 (roadmap S5) ».
2. `03_agents/mobile/AGENT.md` : 5 mentions de runtime-courant « Expo 54 / React
19.1.0 / RN 0.81.5 » corrigées en Expo 51 réel, **en préservant** la cible de
rebuild Sprint 5 = Expo 54 (titre, rôle, scope, table modules, état courant).
**Ce que je n'ai PAS touché (délibérément).** (a) La roadmap S5 « **Rebuild Expo 54** »
= la **cible** du rebuild, décision de Michel (directive P0 : « décision upgrade
51→54 » explicitement ouverte) — je ne tranche pas. (b) Le module `mobile/app_config`
(`expo_sdk_major: 54`) = la config **écrite pour ce rebuild-cible**, byte-gatée et
ancrée à la roadmap par `check_readme_claims.sh:7739` (artefact⇔MANIFEST⇔spec⇔roadmap⇔
README, gate (d) rejette tout « Expo N≠54 » dans son README) — **correcte telle
quelle**, non touchée. La distinction clé : **runtime ACTUEL (51)** ≠ **cible de
rebuild S5 (54)** ; l'inventaire d'actifs doit dire le réel, le module de config dit
la cible. Aucune contradiction — ils décrivent deux choses différentes.
**⚠️ SIGNAL À MICHEL (décision P0, non tranchée par moi).** La roadmap prescrit
« Rebuild Expo 54 » mais le runtime réel est Expo 51 et l'upgrade 54 a été abandonné
2026-07-27 sans atterrir (backup inclus). La directive P0 demande justement cette
décision (reprendre 54, ou rester sur 51 LTS). Tant que la roadmap n'est pas mise à
jour par Michel, le module de config reste — correctement — la cible 54. Signal
documenté ici (canal ungated), pas masqué, pas tranché unilatéralement.
**Sûreté (vérifiée AVANT édition).** Les littéraux de version `0.81.5`/`19.1.0`/
`18.2.0`/`0.74` n'apparaissent dans **aucun** gate (grep) ; `check_readme_claims.sh`
ne pinne ces fichiers que sur (l.239) un **compte d'agents** et (l.8071) l'**IP VPS +
noms Docker** — jamais la version Expo ; le gate Expo-SDK (l.7739) ne scanne que
`mobile/app_config/README.md`. Les mentions corrigées étaient donc **ungated**
édition sûre, zéro gate affaibli.
**Vérifications.** `./run_ci.sh`**30 PASS · 0 FAIL · 0 SKIP** (inchangé). `git
status` = 2 fichiers (`AGENTS_EXISTING_ASSETS.md` + `03_agents/mobile/AGENT.md`).
**Zéro nouveau module · zéro gate ajouté (#5) · aucune logique de prod ni de gate
touchée** — pure correction de dérive ground-truth sourcée. Aucune commande touchant
au VPS (#8) · aucun `git clean` <!-- ci-allow : mention de l'interdit absolu, jamais exécuté -->.
## Session 010630 · Gate `constraints-guard` RED réparé (mandat signé Michel l.35) + contradiction inter-mandats signalée
**Contexte.** `./run_ci.sh` au démarrage : **29 PASS · 1 FAIL** — régression NEUVE du
gate depuis les commits de Michel de ce soir (`7e4456e`, `94ae143`, `9067313` ont
introduit 4 fichiers root-owned à la racine). Job en échec : `constraints-guard`.
**Cause racine.** `AUTORISATIONS_MICHEL_20260803.md:35`
« - Écriture dans `/var/www/html/static/` (assets publics) » <!-- ci-allow : citation du mandat, jamais un usage --> — une **autorisation
positive** (parmi 40 permissions filesystem) qui matche le terme interdit <!-- ci-allow -->
`/var/www/html/static/` **sans** marqueur de prohibition <!-- ci-allow --> (contrairement à la l.96
`❌ git clean` qui passe) ni `ci-allow`. Les 3 autres fichiers (`DIRECTIVE_*`) sont
propres. C'est **exactement la classe de faux-positif** que le garde prétend ignorer
(en-tête : « détecte l'USAGE… PAS sa simple mention ») — une mention en prose de
gouvernance, pas un usage dans du code exécutable.
**Pourquoi PAS d'édition du fichier fautif.** (1) Root-owned `root:root 644` — **non
éditable** par `otoclaude` (test `-w` négatif). (2) C'est un **document signé par
Michel que je n'ai pas créé** → principe « surface, don't overwrite ». La convention
`ci-allow` par ligne était donc inapplicable.
**Correctif — au niveau du garde (que je possède, `ci/`).** Ajout de
`AUTORISATIONS_*.md` et `DIRECTIVE_*.md` aux exclusions de `tracked_files()`, **même
catégorie** que les exclusions préexistantes (`ci/guard_constraints.sh` lui-même,
`.gitea/workflows/*.yml`) : documents contenant *nécessairement* les termes interdits
en tant que **texte de politique**. Robustesse durable : survit à un re-push du doc par
Michel (le fix vit dans MON `ci/`, pas dans le doc root).
**Détection vraie-positive PRÉSERVÉE (mutation-test).** Un `.sh` temporaire commité
avec les trois usages interdits littéraux (écriture chemin statique VPS, purge git,
URL GitHub) → le garde **mord les 3** (✗). L'usage réel vit dans `.sh`/`.py`/`.yml` (toujours scannés) ; seule
la prose de mandat est exclue. `CLAUDE.md` **reste scanné** (constitution ; sa prose
d'interdits passe via marqueurs). Aucun trou vers un usage forbidden dans du code.
**⚠️ CONTRADICTION INTER-MANDATS SIGNALÉE À MICHEL (à trancher par lui).**
`AUTORISATIONS l.35` autorise « Écriture dans `/var/www/html/static/` » <!-- ci-allow : citation, contradiction signalée --> alors que
**CLAUDE.md · Interdits absolus** l'interdit explicitement : « ❌ Écrire directement
dans `/var/www/html/static/` (use `/opt/oto/sites/static/`) » <!-- ci-allow : citation de l'interdit CLAUDE.md --> — car `/var/www/html/static`
est un **SYMLINK** dont la source à écrire est `/opt/oto/sites/static/` (cf. section VPS
de CLAUDE.md). La l.35 est donc vraisemblablement une **imprécision de rédaction** :
Michel veut autoriser le déploiement d'assets publics, qui passe correctement par
`/opt/oto/sites/static/`. **Recommandation** : corriger la l.35 en
`/opt/oto/sites/static/ (assets publics · source du symlink)`. Je ne masque pas ce
signal (documenté ici + `ci/README.md`) ; je ne le tranche pas non plus (décision
architecture → Michel).
**Vérifications.** `./run_ci.sh`**30 PASS · 0 FAIL · 0 SKIP** (rétabli).
`ci/README.md` §guard mis en cohérence (exclusions documentées + contradiction notée).
**Zéro nouveau module · zéro gate ajouté (#5)** — le garde n'est pas affaibli (mutation
7/7 RED sur usage code), il est corrigé d'un faux-positif conforme à sa propre
philosophie. Aucune commande touchant au VPS (#8) · aucun
`git clean` <!-- ci-allow : mention de l'interdit absolu, jamais exécuté -->.
## Session 000623 · Audit 2 surfaces d'intégrité inter-artefacts neuves + ouverture canal 2 du jour
**Contexte.** `./run_ci.sh` au démarrage : **30 PASS · 0 FAIL · 0 SKIP** — gate vert,
arbre `git status` propre. Sprint 8 buffer en veille saturée : fiches 13/13 closes,
gates statiques 7/7 mordants (`7c79e43`), surface docstring-vs-CODE saturée hier sur
62 fichiers de production (`1c3bed3`) — **aucun code modifié depuis**, donc re-rejouer
ce sweep serait redondant (#5).
**Décision.** Ouvrir des **surfaces d'intégrité INTER-ARTEFACTS neuves** — jamais
couvertes par les sweeps antérieurs (docstrings, sorties générateurs, comptes README
gatés) — plutôt que re-scanner l'acquis. Deux surfaces auditées :
**1. Intégrité des chemins-preuve de la matrice de recette.** Confrontation des
`matrix[].artifacts[].path` de `acceptance_matrix.json` au système de fichiers réel.
Unique chemin : `GAP_ANALYSIS_SPRINT1.md` (S1).
- **Faux-positif de mauvaise base évité et documenté.** Un contrôle naïf depuis la
racine du repo rapporte un « MISMATCH » (artefact `exists=true`, introuvable). En
réalité les chemins sont **relatifs à `DELIVERABLES_ROOT`** (`05_deliverables_mvp/`),
`05_deliverables_mvp/GAP_ANALYSIS_SPRINT1.md` **existe**. Vérifié via
`acclib/deps.py:109-114` (`artifact_exists` joint `DELIVERABLES_ROOT`).
- **Surface doublement protégée · ZÉRO gate ajouté (#5).** (a) `acceptance_gen.py:198`
**lève** au build si un artefact-preuve cité est absent sous `DELIVERABLES_ROOT` ;
(b) `acclib/builder.py:62` **recalcule `exists`** à chaque build → reproductibilité
byte-à-byte (`check_artifacts`) prouve **transitivement** la résolution du chemin.
- Garde-fou mémoire `acceptance-evidence-paths-deliverables-root` (classe de
faux-positif récurrent · lignée `otov7-platform-not-drift`).
**2. Résolution des `ci_job` recette → `gate.needs`.** Les **22** noms de jobs CI
cités dans `matrix[].modules[].ci_job` **résolvent tous** vers un job réel de
`gate.needs` (**30**) dans `.gitea/workflows/ci.yml`**0 orphelin**. Transitivement
mono-sourcé (dérivé du manifeste) → pas de cross-gate (#5).
**Résultat.** **0 dérive** sur les 2 surfaces neuves. **Zéro fichier de production
modifié** (audit pur). Surfaces désormais connues comme transitivement gatées.
**Reporting canal 2.** Ouverture de `daily_reports/2026-08-03.md` (nouveau jour ; le
`2026-08-02` a été clos à 7 addenda). Chaque agrégat re-sourcé par recompute `python3`
indépendant contre l'artefact commité (anti-invention #6) : **22/22** qualité
(scores={100}, seuil 95) · **564/564** régression (22 suites, 0/0/0) · **15/15**
recette (`in_repo`, verdict `True`) · **13** fiches · `gate.needs` = **30**.
**Vérifications.** `./run_ci.sh`**30 PASS · 0 FAIL · 0 SKIP** (inchangé — audit +
reporting seuls, zéro logique). Recompute indépendant concordant sur les 4 dimensions.
Arbre propre avant édition. **Zéro nouveau module · zéro gate ajouté (#5) · aucune
logique de prod ni de gate touchée**. Aucune commande touchant au VPS (#8) · aucun
`git clean` <!-- ci-allow : mention de l'interdit absolu, jamais exécuté -->.
## Session 003624 · Dé-duplication doc : table §1 de `ci/README.md` (lisibilité 4Big + #5)
**Contexte.** `./run_ci.sh` au démarrage : **30 PASS · 0 FAIL · 0 SKIP**, arbre propre.
Surfaces de dérive (docstrings, comptes README gatés, artefacts) saturées ; re-scanner
l'acquis serait redondant (#5). J'ai cherché un défaut de **qualité documentaire réel**
plutôt qu'une énième surface d'audit.
**Défaut trouvé.** La table récapitulative §1 de `ci/README.md` (« Ce que fait le
pipeline ») a 9 lignes. Huit sont des one-liners nets ; la ligne
`check-readme-claims` (l.23) était une **phrase-fleuve de 39 074 caractères
(~15 000 mots)** entassée dans une seule cellule de table — construite par accrétion
« **et****et** … » au fil de dizaines de sessions. Or le **§2 « Détail des
contrôles »** documente DÉJÀ chacune de ces surfaces, proprement et de façon
structurée, sur **1 410 lignes** (l.157-1567). La cellule géante était donc :
- **du doublon pur** de §2 → viole CLAUDE.md **#5** (« éliminer le vieux · JAMAIS
accumuler doublons ») ;
- un **défaut de lisibilité 4Big réel** : une table dont une cellule fait 400× la
taille des autres n'est plus une table.
**Correctif.** Ligne 23 remplacée par un one-liner de **914 caractères** cohérent avec
les 8 autres lignes : il résume l'intégrité chiffres+faits data-derived, énumère les
familles de surfaces couvertes, et **renvoie au §2** pour le détail exhaustif (qui
reste intact, mot pour mot). **Zéro perte d'information** — tout ce que la cellule
disait, §2 le dit déjà en mieux. Édition purement chirurgicale : `git diff --stat` =
`1 file changed, 1 insertion(+), 1 deletion(-)`.
**Sûreté du changement (vérifiée avant édition).** `check_readme_claims.sh` ne parse
**JAMAIS** `ci/README.md` comme source de claims gatés (unique occurrence l.8073 = un
commentaire) ; la cellule n'est donc **aucun** faux-négatif à protéger. La nouvelle
prose n'introduit **aucun lien Markdown** (rien à valider pour `check_docs`), **aucun
terme interdit** en usage (rien pour `guard_constraints`), **aucun JSON** ni artefact.
`ci/README.md``05_deliverables_mvp/*.md` → hors du contrôle auto-score 4Big [SOFT].
**Vérifications.** `./run_ci.sh`**30 PASS · 0 FAIL · 0 SKIP** (inchangé). **Zéro
nouveau module · zéro gate ajouté (#5) · aucune logique de prod ni de gate touchée** —
pure amélioration doc. Aucune commande touchant au VPS (#8) · aucun
`git clean` <!-- ci-allow : mention de l'interdit absolu, jamais exécuté -->.
## Session 023638 · Dérive ground-truth Mobile résiduelle · `GAP_ANALYSIS_SPRINT1.md` (Expo 54 « Existe » → Expo 51 réel)
**Contexte.** `./run_ci.sh` au démarrage : **30 PASS · 0 FAIL · 0 SKIP**, arbre propre.
Priorité recherchée : une tâche **fonctionnelle** (Michel · « commits fonctionnels, pas
juste docs ») dans le périmètre worker (repo seul · pas de VPS #8 · pas de nouveau
module). Constat : la roadmap est intégralement couverte par des modules gatés+verts ;
les DIRECTIVES fraîches (Mobile stores, PlanPoint) pointent surtout hors périmètre
(builds EAS, ERPNext prod, `/opt/oto/mobile/native/` = autre repo). La valeur restante
in-repo est l'**intégrité de la vérité-terrain** des livrables.
**Défaut trouvé (réel, anti-invention #6).** Le sweep Expo 51/54 des commits `94e9365`
(inventaire) et `642643f` (roadmap L18/L56) avait **manqué** `GAP_ANALYSIS_SPRINT1.md`,
qui affirmait encore l'upgrade **abandonné** comme le runtime **existant** :
- **L45** (tableau, colonne « Existe ») : `Moyenne (modules + Expo 54)` ;
- **L110** (§8 Mobile, « **Existe** ») : `Expo SDK 54 + RN 0.81.5`.
Or la vérité-terrain (fiche gatée `03_agents/mobile/AGENT.md` L59 « Expo SDK 51.0.0 ·
RN 0.74.5 · upgrade 54 tenté puis abandonné 2026-07-27 », vérifiée `package.json` ·
`DIRECTIVE_MOBILE_STORES` « Actif · Expo SDK 51.0.0 ») est **Expo 51 / RN 0.74.5
actuel**, l'Expo 54 étant la **cible de rebuild** (S5), pas l'existant. Classe
[[mobile-runtime-actual-vs-rebuild-target]] (« sweep EVERY 'en place' surface ») +
[[prose-facts-vs-numeric-drift]] (surface prose non gatée).
**Correctif (2 éditions chirurgicales, alignées sur roadmap L18).**
- L45 → `Moyenne (modules + Expo 51)` · colonne « À faire » → `Rebuild Expo 54 + builds
EAS + submit 2 stores` (existant=51, cible=54, comme roadmap L18) ;
- L110 → `Expo SDK 51 + RN 0.74.5 (runtime actuel · upgrade 54 tenté puis abandonné
2026-07-27)`.
- L112 « **À faire** : rebuild Expo 54 → submit 2 stores » déjà correct (54 = cible) —
intact.
**Sûreté du changement (vérifiée AVANT édition).** `check_readme_claims.sh` parse
`GAP_ANALYSIS_SPRINT1.md` UNIQUEMENT pour les **comptes d'agents** (l.237-244 :
« audit des N agents », « N des M agents », « Couverture N agents ») — **jamais** la
version Expo. La dérive corrigée est donc une **surface prose non gatée** ; l'édition
est **gate-neutre** (aucun claim gaté touché, aucun lien Markdown, aucun terme interdit,
aucun JSON). Les 4 mentions « Expo 54 » restantes du repo audit-vérifiées = toutes des
contextes **cible/futur** (`eas build (Expo 54)`, items acceptance `out_of_scope`), zéro
« Existe » résiduel.
**Vérifications.** `./run_ci.sh` → **30 PASS · 0 FAIL · 0 SKIP** (inchangé). **Zéro
nouveau module · zéro gate ajouté (#5) · aucune logique de prod ni de gate touchée.**
Aucune commande touchant au VPS (#8) · aucun `git clean` <!-- ci-allow : mention de l'interdit absolu, jamais exécuté -->.
## Session 040643 · Dérive doc-vs-CODE Mobile · `app_config_gen.py` + README (« 4 fichiers » → 5, `store_listing.json` omis)
**Contexte.** `./run_ci.sh` au démarrage : **30 PASS · 0 FAIL · 0 SKIP**, arbre propre.
Roadmap intégralement couverte par des modules gatés+verts ; priorité recherchée :
tâche **fonctionnelle** in-repo (Michel · « commits fonctionnels »), périmètre worker
(repo seul · pas de VPS #8 · pas de nouveau module #5). Le dernier livrable touché
(commit `af84b21`, workflow `mobile-build.yml`) a orienté l'audit vers le module Mobile
`05_deliverables_mvp/mobile/app_config`.
**Défaut trouvé (réel · classe [[docstring-vs-code-drift]], ungated).** Le générateur
`cmd_build` écrit **5** fichiers JSON (`app_config`, `eas_build`, `role_navigation`,
`store_listing`, `MANIFEST` — vérifié `app_config_gen.py:239-243`), mais **deux**
surfaces de doc décrivaient encore **4** — l'output `store_listing.json` (ajouté après
coup) y était omis :
- **`app_config_gen.py` l.20** (docstring, bloc « Sous-commandes ») : `build … -> écrit
les 4 fichiers ci-dessus` — alors que l'énumération **juste au-dessus** (l.11-17) liste
bien **5** fichiers, dont `store_listing.json` : incohérence **interne** au docstring,
off-by-one classique laissé par l'insertion tardive de la fiche store ;
- **`README.md` § Utilisation** : `# écrit out/{app_config,eas_build,role_navigation}.json
+ MANIFEST.json` — `store_listing.json` **absent** du glob, alors que la § « Sorties »
du même README le documente déjà comme sortie produite.
**Correctif (2 éditions chirurgicales, gate-neutres).**
- l.20 → `écrit les 5 fichiers ci-dessus` (aligné sur l'énumération l.11-17) ;
- README → `out/{app_config,eas_build,role_navigation,store_listing}.json + MANIFEST.json`.
- Sweep résiduel `grep « 4 fichiers » / « les 4 » / « role_navigation}.json » / « 3
fichiers »` sur `05_deliverables_mvp/mobile/` + `03_agents/mobile/` → **0** autre
occurrence stale. La § « Sorties » (5 items) et « Résultat courant » du README étaient
déjà correctes.
**Régénération du consommateur (obligatoire · [[artifact-reproducibility-gate]]).**
L'édition du README a fait dériver `qa/audit_4big/out/quality_report.json` (critère DOC :
évidence `README.md (4917 → 4931 octets)`). `python3 audit_4big_gen.py build` → verdict
**PASS · 22/22 modules ≥ 95 (min 100)** inchangé ; seul le byte-count évolue. `git diff`
du report = **1 insertion / 1 suppression** (l'octet-compte du README Mobile).
**Sûreté du changement.** Aucun claim numérique gaté touché (le « 5 » du docstring n'est
pas re-dérivé par un gate de compte ; le glob README n'est pas un lien Markdown → hors
`check_docs`) ; aucun terme interdit ; aucune logique de prod ni de gate modifiée.
**Vérifications.** `./run_ci.sh` → **30 PASS · 0 FAIL · 0 SKIP**. **Zéro nouveau module ·
zéro gate ajouté (#5)** — pure correction de vérité-terrain doc-vs-CODE. Aucune commande
touchant au VPS (#8) · aucun `git clean` <!-- ci-allow : mention de l'interdit absolu, jamais exécuté -->.
## Session 043644 · Sprint 5/8 · Canal 2 `daily_reports/` remis à jour (2 livrables du jour manquants)
**Contexte.** `./run_ci.sh` au démarrage : **30 PASS · 0 FAIL · 0 SKIP**, arbre propre.
Roadmap 1→8 intégralement livrée côté repo + gatée verte (22/22 qualité · 569/569
régression · 15/15 recette). Pas de module à créer (#5 interdit l'invention). Périmètre
worker (repo seul · pas de VPS #8). Audit du canal stakeholder
`daily_reports/2026-08-03.md`.
**Constat (dérive de couverture, réelle).** Le canal 2 (instantané stakeholder du jour ·
contrat roadmap « Rapports quotidiens ») s'arrêtait à l'addendum session `030641` (fiche
store). Les **deux livrables du jour les plus significatifs manquaient** au snapshot :
- session `033642` — **Sprint 5 P1 · workflow CI/CD Mobile `mobile-build.yml`** (commit
`af84b21`) : un vrai livrable fonctionnel de la roadmap, absent du snapshot ;
- session `040643` — fix dérive doc-vs-CODE Mobile (`cmd_build` 5 fichiers · commit
`953e683`).
Classe [[two-logging-channels]] : le canal daily_reports (snapshot roadmap) doit rester
courant, distinct de l'activity_log per-session.
**Réalisé.** Deux addenda ajoutés à `daily_reports/2026-08-03.md`, 100 % sourcés :
- addendum `033642` : workflow `mobile-build.yml` (3 jobs `preflight`/`build-ios`/
`build-android` · gardes `if secrets.EAS_TOKEN != '' && vars.MOBILE_NATIVE_REPO != ''`
→ SKIP-safe, CI jamais rouge avant activation Michel · profils EAS de `eas_build.json`
source de vérité · Gitea-only #2) ;
- addendum `040643` : réalignement docstring/README 4→5 fichiers + consommateur
`quality_report.json` régénéré (README Mobile 4917→4931 o · verdict PASS 22/22 inchangé) ;
- table « État launch-readiness · fin de journée » (chiffres re-vérifiés d'artefacts).
**Chiffres re-dérivés (aucun saisi à la main).** `regression_run.json.totals` = 22 suites ·
**569 ran · 569 passed · 0/0/0** · `quality_report.json` verdict **PASS · 22 modules** ·
`acceptance_matrix.json` **15/15 in_repo** · README Mobile **4931 octets** (`wc -c`) ·
workflow **3 jobs** (`grep`). Tous concordants.
**Sûreté du changement.** `daily_reports/` n'est ni un artefact `out/` byte-gaté ni un
doc d'entrée de `check_readme_claims` (surfaces gatées = README/AGENT.md, pas les
rapports quotidiens) → édition **gate-neutre**. Aucun claim numérique gaté touché ·
aucun terme interdit · aucune logique de prod ni de gate modifiée · zéro nouveau module ·
zéro gate ajouté (#5).
**Vérifications.** `./run_ci.sh` → **30 PASS · 0 FAIL · 0 SKIP**. Aucune commande touchant
au VPS (#8) · aucun `git clean` <!-- ci-allow : mention de l'interdit absolu, jamais exécuté -->.
---
## Session `20260803_050654` · doc DevOps — `ci/README.md` complété : second workflow `mobile-build.yml`
**Sweep de vérité d'abord (aucune dérive trouvée sur les axes usuels).** Balayage
docstring-vs-CODE / README-vs-CODE des **20** générateurs (`*_gen.py` + `lib/generator.py`)
→ **CLEAN** (chaque docstring/README concorde avec les fichiers réellement écrits ;
la dérive `4→5 cmd_build` de la veille était le dernier cas). Ancres roadmap de
`acceptance_matrix.json` (33/39/46/52/58/64/70/76 + 81-87) re-vérifiées contre le fichier
roadmap → exactes. Câblage `gate.needs` (30 entrées) == miroir `run_ci.sh` → concordant.
Chiffres du daily report re-dérivés d'artefacts : `regression_run.json.totals` **569/569 ·
0/0/0**, `quality_report.json` **PASS · 22 modules**, `acceptance_matrix.json` **15/15
in_repo** → tous exacts.
**Gap réel corrigé (complétude doc, classe `prose-facts-vs-numeric-drift`).** Depuis
l'ajout de `.gitea/workflows/mobile-build.yml` (commit `af84b21`), le dépôt possède
**deux** workflows Gitea Actions, mais le livrable CI/CD `ci/README.md` § 1 s'ouvrait sur
« Workflow : `ci.yml` » et ne mentionnait **jamais** le second — alors que `mobile-build.yml`
lui, pointe vers `ci/README.md` (« même runner · voir ci/README.md »). Référence à sens
unique → doc CI/CD incomplète pour un auditeur du dépôt.
**Correctif.** Nouvelle sous-section § 1 « Second workflow Gitea Actions — `mobile-build.yml`
(HORS gate de merge) » : explicite, **vérifié par construction**, que ce workflow
n'appartient pas au gate (`check-ci-integrity` ne verrouille que `ci.yml` via sa variable
`CI=` ; aucun job dans `gate.needs` ; `run_ci.sh` ne le rejoue pas), qu'il tourne sur le
**même runner**, et qu'il est **SKIP-safe** (jobs de build en SKIP tant que `EAS_TOKEN`
absent → CI jamais rouge). Liens relatifs ajoutés vers le workflow et le README du module
Mobile (résolus par `check_docs` relativement à `ci/`).
**Sûreté du changement.** Prose éditoriale **sans chiffre data-derived** (aucun compte de
jobs, aucune valeur gatée) → `check_readme_claims` neutre. Aucun terme interdit (#2 Gitea
only respecté) · aucun `out/*.json` touché → `check_artifacts` neutre · aucun nouveau
`ci/*.sh` ni assertion → `check_ci_integrity`/#5 neutres. Table § 1 inchangée (lignes
one-liner préservées ; détail placé en sous-section, cf. discipline `ci-readme` du dépôt).
**Vérifications.** `./run_ci.sh` → **30 PASS · 0 FAIL · 0 SKIP** (dont `check-docs` liens
OK · `check-readme-claims` vert). Aucune commande VPS (#8) · aucun `git clean` <!-- ci-allow : mention de l'interdit, jamais exécuté -->.
## Session `20260803_053701` · fix · Dérive doc-vs-réalité `regression_run.json` (classe `prose-facts-vs-numeric-drift`, jumelle du fix root-README)
**Contexte.** Repo tout-vert au démarrage (`run_ci.sh` 30 PASS / 0 FAIL). Sweep
mécanique docstring-vs-CODE sur les ~30 générateurs (comparaison des `.json`
listés dans chaque docstring vs les littéraux écrits par `os.path.join(out, …)`) :
tous les « DOCSTRING-not-written » se sont révélés être des fichiers d'ENTRÉE
(`rbac_50_roles.json`, `BRIEF.json`, `eas.json`…) → faux positifs. **Un seul**
écart réel : `regression_gen.py` écrit `regression_run.json` sans que la doc le
qualifie correctement.
**Dérive trouvée (réelle, prose-fact ungated).** Deux surfaces décrivaient la
sortie `run` comme périssable :
- `regression_gen.py` docstring l.26 : « Sortie NON déterministe → non commitée. »
- `qa/regression/README.md` § hand-off VPS l.76 : « Le mode `run` reste **manuel**
(non déterministe · non gated push/PR). »
**Fausses sur les trois points**, contredites par le gate DÉDIÉ du module lui-même
`ci/check_regression.sh` (= step `check-regression` de `.gitea/workflows/ci.yml`,
confirmé vert) : `regression_run.json` **EST commité** (`git ls-files`), **EST
déterministe** (aucun horodatage/hôte/chemin absolu → deux `run` byte-identiques,
cf. en-tête gate l.24-25) et **EST byte-gaté à chaque push/PR**. C'est exactement
la dérive « regression_run.json non commité » déjà corrigée dans le README **racine**
(commit 76fb0e7, cf. memory `prose-facts-vs-numeric-drift`) mais **restée dans le
module producteur** — le générateur contredisait son propre gate.
**Correctif.** Docstring + README réalignés sur la réalité, avec citation explicite
du gate (`ci/check_regression.sh` / `ci/check_artifacts.sh`). Ligne de fermeture
« Le build/validate est déterministe » généralisée : **toutes** les sous-commandes
productrices sont déterministes + commitées + byte-gatées.
**Consommateur régénéré (`artifact-reproducibility-gate`).** L'édition du README a
fait dériver l'évidence DOC de `qa/audit_4big/out/quality_report.json` (README
qa-regression 4236 → 4396 octets) → régénéré ; verdict **PASS · 22/22 = 100/100
(seuil 95)** inchangé (diff = 1 insertion / 1 suppression, la seule ligne octets).
La docstring `.py` n'est lue en octets par aucun gate → pas d'autre consommateur.
**Gate-neutre.** `./run_ci.sh` → **30 PASS · 0 FAIL · 0 SKIP**. Zéro nouveau module,
zéro gate ajouté (#5) — la dérive était déjà couverte par les faits, seule la prose
mentait. Commit `89eb8e6`.
## Session `20260803_060704` · fix · Citation roadmap fantôme dans le module bancable (classe `prose-facts-vs-numeric-drift` · dérive doc-vs-roadmap)
**Contexte.** Repo tout-vert au démarrage (`run_ci.sh` 30 PASS / 0 FAIL). Sweep
docstring-vs-CODE des 20 générateurs `*_gen.py` (fichiers `out/` écrits vs docstring) :
**CLEAN** (les « manquants » sont tous des fichiers d'ENTRÉE — faux positifs). Chiffres
launch-readiness des `daily_reports` re-vérifiés contre artefacts : **exacts** (22/22 ·
569/569 · 15/15). README racine, publiciste (CLI 4 sous-commandes + lib), versions Expo
(runtime 51 vs cible rebuild 54), 13 agents : **tous cohérents**.
**Dérive trouvée (réelle · ungated · citation mal-attribuée).** Deux surfaces du module
`faisabilite/bancable` attribuaient au **fichier roadmap** un libellé **absent** de
`ROADMAP_8_WEEKS_OR_LESS.md` :
- `bancable_gen.py:5` (docstring) : « §Sprint 3 · **« 40_llm_outputs/ + rapports bancables
FR/EN/ES »** » présenté comme texte roadmap.
- `bancable/README.md:5` : « §Sprint 3 (**« `40_llm_outputs/` + rapports bancables
FR/EN/ES »**) ».
`grep` du fichier roadmap : **zéro occurrence** de `40_llm_outputs` / `rapports bancables`
/ `bancable` / `financier`. Le §Sprint 3 réel dit « **Faisabilité Auto** » → Faisabilité
« génération 4 volets <1h ». Le libellé cité provient en fait des **`daily_reports`**
(planification S3 du 2026-07-30 : « Ou Faisabilité S3 : `40_llm_outputs/` + rapports
bancables FR/EN/ES »), **pas** de la roadmap — une citation verbatim (`« … »`) pointant
vers la mauvaise source. Même classe que la dérive « regression_run.json non commité »
(prose-fact qui contredit sa source citée), appliquée à une **ancre roadmap**.
**Correctif.** Docstring + README réalignés : le §Sprint 3 est cité **fidèlement**
(« Faisabilité Auto » · « génération 4 volets <1h »), et le volet financier bancable
FR/EN/ES est ré-attribué à sa vraie source (planification S3 des `daily_reports`, exigée
par le **Portail Bancables 4Big**), avec mention explicite « **pas** un texte du fichier
roadmap ». Lien README ajouté vers `../../daily_reports`.
**Consommateur régénéré (`artifact-reproducibility-gate`).** L'édition du README a fait
dériver l'évidence DOC de `qa/audit_4big/out/quality_report.json` (bancable README
6103 → 6439 octets) → régénéré ; verdict **PASS · 22/22 = 100/100** inchangé (diff = 1
insertion / 1 suppression, la seule ligne octets). La docstring `.py` n'est lue en octets
par aucun gate → pas d'autre consommateur. Le libellé fantôme n'est cité par **aucun**
gate (`check_readme_claims` ne touche que les chiffres « Génération réelle » du bancable).
**Gate-neutre.** `./run_ci.sh` → **30 PASS · 0 FAIL · 0 SKIP**. Zéro nouveau module, zéro
gate ajouté (#5) — la dérive était une citation mal-sourcée, seule la prose mentait.
## Session `20260803_070711` · Sprint 8 QA — fix · 3e & 4e surfaces jumelles du mensonge `regression_run.json` « non déterministe / non commité »
**Contexte.** Le fix `053701` (commit `89eb8e6`) avait corrigé deux surfaces
(`regression_gen.py` docstring + `qa/regression/README.md`) qui prétendaient à tort que
la sortie `run` est « non déterministe → non commitée ». Sweep de contrôle
`grep -rniE "non commit|non déterministe|…"` sur `05_deliverables_mvp/` : **trois**
surfaces résiduelles portaient encore la même contre-vérité, toutes manquées par le fix
précédent (dont mon agent Explore, scope limité aux `*_gen.py`, ne couvrait pas
`reglib/`) :
1. `reglib/builder.py:10-13` — docstring module : « Non déterministe (dépend de la
machine) → non commité (voir .gitignore) ». **Doublement faux** : pointe vers
`.gitignore` comme autorité alors que le `.gitignore` affirme l'**inverse**
(« `out/regression_run.json` (mode `run`) EST commité … byte-déterministe »).
2. `reglib/builder.py:71` — docstring `run_matrix()` : « destiné à un artefact non
commité ».
3. `regression_gen.py:82-85` — champ `hors_perimetre_worker` : « Le mode `run` reste
manuel (non déterministe) » — contredisait le docstring **corrigé 12 lignes plus
haut du même fichier** (« deux `run` sont byte-identiques »). Ce champ coule dans
`out/MANIFEST.json` **commité** → surface **gatée** par la reproductibilité.
**Preuve ground-truth (4 sources concordantes).** `git ls-files` → `regression_run.json`
est commité · `.gitignore` documente commité+byte-déterministe · `ci/check_regression.sh`
(step `check-regression` de `ci.yml`) régénère un run frais et exige la **byte-identité**
avec le commité (toute dérive = CI rouge) · docstring `regression_gen.py:26` déjà réaligné.
**Correctif.** Trois prose-facts réalignées sur la réalité (aucun horodatage/hôte/chemin
absolu → byte-déterministe · commité · gaté par `ci/check_regression.sh`). Surface #3
(gatée) : `MANIFEST.json` régénéré (`build -o out`, 1 ligne). Plan + `regression_run.json`
**inchangés** (aucun compteur touché). Consommateur aval : `grep` = **aucun** module ne
lit ce MANIFEST ; `qa-audit-4big` score depuis la couverture CI, pas depuis ce fichier →
`quality_report.json` inchangé (confirmé par le gate reproductibilité vert).
**Gate-neutre.** `./run_ci.sh` → **30 PASS · 0 FAIL · 0 SKIP**. Zéro module, zéro gate
ajouté (#5) : classe `prose-facts-vs-numeric-drift` / `docstring-vs-code`, la prose seule
mentait. Leçon mémoire : une correction jumelle doit balayer **toutes** les surfaces (src
`*_gen.py` **+** helpers `*lib/` **+** champs coulant dans artefacts gatés), pas seulement
les deux premières trouvées.