# Activity Log · 2026-08-05 · Claude Code DTP Worker ## 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 un `if:` de **job** (`^ if:`). Le contexte `secrets` n'est pas exposé dans `jobs..if` (table d'availability GitHub Actions) → sur `act`/Gitea il s'évaluerait vide → **SKIP silencieux**. C'est la régression `234104` verrouillée. - **MOB-3 · activation différée (#6/#8)** — chaque job de build (`run: eas build`) est gaté sur `needs.preflight.outputs.has_token` → SKIP tant que le token absent, donc le workflow ne rend jamais le CI rouge avant activation Michel. - **MOB-4 · contrat d'outputs** — `preflight` déclare `has_token`/`has_repo`, chaque `needs.preflight.outputs.` référencé est **déclaré** (0 dangling) et chaque output déclaré est **alimenté** par un `echo …>>$GITHUB_OUTPUT`. **Bug corrigé pendant l'écriture (auto-vérification).** 1er jet : `preflight` était classé « job de build » car un **commentaire** `# … eas build …` (l.108, entre deux jobs) était attribué au job précédent par le parseur awk. Corrigé en exigeant `run:[[:space:]]*eas build` (commande réelle, pas une mention). Après correctif : gate **VERT** sur le fichier propre. **Mutation-testing du gate lui-même (5 mutations · toutes ROUGES, fichier propre re-VERT).** (1) YAML cassé (indent) · (2) `secrets.` en `if:` de job · (3) `if:` du job `build-ios` supprimé · (4) output fantôme référencé (`has_tokenX`) · (5) output `has_repo` non alimenté. Chaque mutation → `exit 1` ; `cp` backup → gate re-VERT. **Câblage (garde `check-ci-integrity` INV-A/B vert).** Job `check-mobile-workflow` ajouté à `ci.yml` (steps `bash ci/check_mobile_workflow.sh`) **et** à `gate.needs`. `check_ci_integrity` reconnaît le nouveau `ci/*.sh` comme **câblé** (lancé par un job du gate + il source `lib.sh`) → INV-A (gate.needs == tous jobs non-manuels) et INV-B (tout `ci/*.sh` câblé) **restent verts**. `run_ci.sh` (dérivé de `gate.needs`, sans liste en dur) l'inclut **automatiquement** → passage **32→33 jobs (8 gates statiques + 25 suites)**, zéro édition de `run_ci.sh` (#6, exactitude structurelle). **Doc (traçabilité, pas de dérive).** `ci/README.md` : ligne §1 pour `check-mobile-workflow`, sous-section §2 détaillée (4 invariants + les 5 mutations), et nuance ajoutée à « Second workflow » (les **jobs** restent hors gate de merge, mais la **structure du fichier** est désormais gatée). Convention [[ci-readme-table-detail-in-section2]] respectée : ligne §1 courte, détail en §2. Les mentions historiques « 7 gates re-verts » (§2, records de mutations passées) restent exactes en **passé** ; le nouveau record dit **8ᵉ gate**. **Vérifications.** `bash ci/check_mobile_workflow.sh` → VERT (4 invariants) ; 5 mutations → ROUGE ; `python3 yaml.safe_load` sur `ci.yml` → valide ; `check_ci_integrity` → VERT (câblage intègre) ; `./run_ci.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 · verdict `PASS`. - `regression_run.json.totals` : **24 suites · 624 ran · 624 passed · 0/0/0** · verdict `PASS`. - `acceptance_matrix.json` : **15/15 `status=in_repo`** · verdict `True` · **`ci_job` ×36** (niché `matrix[].modules[]`). - Structure CI : `run_ci.sh --list` = **33 jobs = 8 statiques + 25 suites** · `ls ci/*.sh` (hors `lib.sh`) = **8**. **Vérifications.** `./run_ci.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`.**