Compare commits

..

59 Commits

Author SHA1 Message Date
Claude Code DTP Worker 9d30939db9 [DTP-Worker 20260805_161319] Auto exec · session 20260805_161319
CI / Contraintes NON-NÉGOCIABLES (CLAUDE.md) (push) Has been cancelled
CI / Validation JSON (schémas Faisabilité) (push) Has been cancelled
CI / Qualité documentaire (liens + 4Big) (push) Has been cancelled
CI / Reproductibilité des artefacts out/ (build == commité) (push) Has been cancelled
CI / Fraîcheur matrice de régression (run == commité) (push) Has been cancelled
CI / Intégrité du câblage CI (gate agrège tout · gates statiques verrouillés) (push) Has been cancelled
CI / Intégrité des chiffres du README (valeur == artefact cité · (push) Has been cancelled
CI / Intégrité mobile-build.yml (gating portable · activation différée · (push) Has been cancelled
CI / Publiciste · parser + schéma + generator (unittest) (push) Has been cancelled
CI / RBAC · 50 rôles + schéma (unittest) (push) Has been cancelled
CI / Faisabilité · générateur 4 volets + round-trip (unittest) (push) Has been cancelled
CI / RBAC · fixtures ERPNext (Role + Custom DocPerm) (push) Has been cancelled
CI / RBAC · plan User Permission (row-level) (push) Has been cancelled
CI / RBAC · Role Profile (bundles par portail) (push) Has been cancelled
CI / RBAC · run-book d'application unifié (agrégat 3 volets) (push) Has been cancelled
CI / Faisabilité · dossier bancable trilingue FR/EN/ES (push) Has been cancelled
CI / CRM · workflow vente ERPNext (lead → CONFOTUR) (push) Has been cancelled
CI / CRM · DocType porteur OTO Dossier Vente (push) Has been cancelled
CI / CRM · barème commissions vendeurs (push) Has been cancelled
CI / CRM · Financement Bancaire (gate hypothécaire RD) (push) Has been cancelled
CI / Fiscal · e-CF DGII (Compupar) (push) Has been cancelled
CI / Frontend · Workspaces 5 portails rôle (push) Has been cancelled
CI / Legal · DocType CONFOTUR Application (push) Has been cancelled
CI / QA · Audit 5D conformité (push) Has been cancelled
CI / SEO · mots-clés trilingues + schema.org + hreflang (push) Has been cancelled
CI / Chat OTOIA · montage par portail (Custom Block) (push) Has been cancelled
CI / QA · Audit 4Big (95+/100 sur 100% deliverables) (push) Has been cancelled
CI / Démo · Scénarios (run-sheet P07 banquier / P05 client) (push) Has been cancelled
CI / QA · Matrice de régression exhaustive (Sprint 8) (push) Has been cancelled
CI / DevOps · Run-book de déploiement VPS unifié (Sprint 8) (push) Has been cancelled
CI / QA · Matrice d'acceptation / traçabilité MVP (Sprint 8) (push) Has been cancelled
CI / Mobile · config app Expo/EAS (navigation par rôle) (push) Has been cancelled
CI / PIE · manifest de dépendances (Annexe 12 · V10.1) (push) Has been cancelled
CI / E2E baseline Playwright (manuel) (push) Has been cancelled
Mobile Build (EAS) / Préflight config EAS + état secrets (push) Has been cancelled
CI / Gate qualité (agrégat) (push) Has been cancelled
Mobile Build (EAS) / EAS build iOS (App Store (push) Has been cancelled
Mobile Build (EAS) / EAS build Android (Play Store) (push) Has been cancelled
2026-08-05 16:28:19 +00:00
Claude Code DTP Worker 731ff6b65d [DTP-Worker 20260805_154314] SWEEP neuf indépendance LOCALE/TIMEZONE : les 21 générateurs build rejoués × 5 combos LANG/LC_ALL/TZ (C·UTC → de_DE·Kolkata → POSIX·Kiritimati) = 105 rejeux diff vs commité → CLEAN 0 dérive. 3e axe déterminisme après hash-seed (PYTHONHASHSEED) et forward-compat (-W error), même famille « le runner n'est pas ton poste », cause différente (locale/fuseau).
Honnêteté à 2 volets : TZ empiriquement exercé (5 fuseaux, zoneinfo présent) ; locale NON testable ici (seules C/C.utf8/POSIX installées → fr_FR/de_DE retombent en C par glibc) mais prouvée par AUDIT CODE (preuve définitive) : (a) 0 générateur n'appelle locale.setlocale → json/float reste '.' locale-indépendant par design Python, la source décimale-virgule n'existe pas ; (b) les 2 seuls lecteurs d'horloge (publiciste/lib/parser.py, faisabilite/generator/faisabilite_gen.py) n'ont AUCUN out/ commité + forcent timezone.utc (doublement TZ-safe) ; (c) regression_run.json sans champ horloge. Runner robuste sous toute locale/fuseau.

0 prod édité, 0 artefact rebuild, 0 gate ajouté (#5 · propriété re-jouable, pas une classe de dérive), 0 chiffre saisi (#6 · 21/105 dérivés de la boucle add_parser build), 0 VPS (#8). Audit fan-out docstring-vs-code + prose-vs-numérique = CLEAN au passage. run_ci 33 PASS. Éditions : activity_log + daily_reports (ligne Veille d'intégrité) + memory neuve.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 15:55:28 +00:00
Claude Code DTP Worker a39c05aeda [DTP-Worker 20260805_151312] SWEEP neuf déterminisme hash-seed : les 21 générateurs build (57 artefacts) rejoués sous 5 PYTHONHASHSEED (1/7/42/1000/random) + regression_run.json sous 6 rejeux → diff vs commité = CLEAN 57 byte-identiques, 0 dépendance à l'ordre d'itération set/dict.
Classe absente de la mémoire, orthogonale aux axes teeth ET au sweep forward-compat warnings : même famille faux-vert FORWARD, cause différente (ordre de hash vs API dépréciée). check_artifacts/check_regression prouvent le rebuild byte-identique MAIS ne tournent QU'UNE fois, sous LE seed que le runner Gitea donne (randomisé par défaut depuis Python 3.3). Un seul générateur sérialisant un set/dict en ordre d'itération → gate VERT aujourd'hui, ROUGE au hasard demain sur le runner. Sweep prouve : aucun ne le fait → les deux gates de reproductibilité sont robustes au hash-seed randomisé.

Harnais auto-vérifié (57 comparés · 1 skip = regression_run.json sous la passe build-only = l'exclusion documentée de check_artifacts, pas un skip silencieux). 0 fichier de prod édité (rebuilds → /tmp), 0 artefact reconstruit, 0 gate ajouté (#5 · propriété re-jouable via commande, pas classe de dérive), 0 chiffre saisi (#6 · 21/57 dérivés de find+décompte), 0 VPS (#8 · 100% local lecture seule). run_ci 33 PASS. Éditions : activity_log + daily_report (table veille) + mémoire neuve.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 15:19:24 +00:00
Claude Code DTP Worker 2337833e5a [DTP-Worker 20260805_144304] CURRENCY canal stakeholder + consignation d'un incident d'intégrité absent du daily_report
Le daily_report 2026-08-05 s'arrêtait à la session 054200 et laissait croire à une journée VERTE ininterrompue. Or 7 commits ont suivi, dont UN matériel pour le stakeholder : 127ef6f (124241) annonçait « 33 PASS » mais commitait un tree RED (32 PASS·1 FAIL) — guard_constraints/constraints-guard mordant sur le tree commité lui-même — rattrapé par 1321983 (131244) sous ~50 min (tree revenu VERT). La ré-attestation 9b6ec60 (121234) avait confirmé le canal courant mais n'éditait QUE le journal, pas le fichier daily_report → l'incident RED→VERT restait absent du canal stakeholder.

FIX (currency channel, 0 prod, 0 artefact rebuild) : section fin-de-journée 144304 ajoutée au daily_report documentant (a) l'incident RED→VERT = preuve que la discipline de gate est opposable au tree commité (pas seulement pré-commit local), (b) les 2 sweeps lecture-seule suivants (docstring-vs-code 48b19b9 CLEAN 26 modules · forward-compat warnings f311a1b CLEAN 25/25/624), (c) ré-attestation HEAD f311a1b.

Recompute python3 indépendant (aucun chiffre saisi · #6) : quality_report 24 modules tous==100 coverage.ok verdict PASS · regression_run totals suites 24/ran 624/passed 624/0-0-0 · run_ci 33 PASS=8 statiques+25 suites — toutes tables CONCORDENT. Gates re-joués sur l'arbre édité : check_docs OK, guard_constraints OK. 0 gate ajouté (#5 · intégrité déjà gatée par check_ci_integrity+guard_constraints), 0 VPS (#8). Editions : daily_report (section 144304) + activity_log. Classe two-logging-channels.
2026-08-05 14:47:50 +00:00
Claude Code DTP Worker f311a1bad9 [DTP-Worker 20260805_141301] SWEEP neuf forward-compat warnings : 25 suites de gate.needs rejouées sous -W error (Deprecation/Syntax/Pending/Resource promus en erreurs) → CLEAN 25/25 · 624 tests · 0 warning latent sous Python 3.10.12. Classe absente de la mémoire, orthogonale aux axes teeth : un warning émis silencieusement aujourd'hui reste VERT (warning != erreur) mais casse tout seul au prochain bump d'interpréteur = faux-vert FORWARD invisible aux tables de currency. Corpus prêt au durcissement d'interpréteur sans régression silencieuse (aucune API dépréciée, aucun handle fuité). Root tests/ = Playwright e2e (job manuel hors gate) → 0 test python attendu. 0 prod, 0 artefact rebuild, 0 gate ajouté (#5 · propriété re-jouable via la commande, pas une classe de dérive), 0 chiffre saisi (#6), 0 VPS (#8). run_ci 33 PASS. Editions : activity_log + memory neuve. 2026-08-05 14:18:38 +00:00
Claude Code DTP Worker 48b19b9a52 [DTP-Worker 20260805_134254] SWEEP read-only docstring-vs-code drift sur 26 modules → CLEAN
Balayage lecture seule de la SEULE classe que la mémoire marque `RECURRING / not-closed`
(docstring-vs-code-drift) : fan-out Explore sur les 26 modules générateurs (*_gen.py +
lib/*.py + textes argparse embarqués), directions A (advertise non-implémenté) ET B (omet
un output réel) couvertes. Résultat : 0 dérive réelle. L'unique item remonté est EXACTEMENT
le faux-positif déjà refermé 2× (deploy_runbook_gen.py:28 `validate [-o OUT]`, help="ignoré
(compat)" :288) — re-signalé une 3e fois par un Explore neuf MALGRÉ l'amorce de SKIP. Vérifié
en 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 ; seul `build` (:27) revendique l'écriture,
ce que le code fait. Bijection parfaite self-cohérente → NON-défaut, LEAVE (#5), 0 édition doc.

Portée : 0 fichier de prod édité, 0 artefact reconstruit, 0 gate ajouté (#5), 0 chiffre
inventé (#6), 0 commande VPS (#8), sweep 100% lecture. Seule édition : activity_log.
Vérif : ./run_ci.sh → 33 PASS · 0 FAIL · 0 SKIP. Mémoire docstring-vs-code-drift enrichie
(3e récurrence documentée : faux-positif essentiellement insupprimable par amorce — l'attendre
comme unique hit et confirmer-puis-écarter, ne pas le traiter comme signal).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 13:47:54 +00:00
Claude Code DTP Worker 1321983dc3 [DTP-Worker 20260805_131244] FIX régression gate constraints-guard : tree committé RED (32 PASS·1 FAIL) malgré « 33 PASS » annoncé par 127ef6f
Au démarrage ./run_ci.sh échouait sur constraints-guard : ligne 1206 de l'activity_log 2026-08-05 cite « aucun `git clean` » (#1) sans marqueur ; le `ci-allow` posé par la session précédente était sur la ligne 1207 (paragraphe hard-wrappé) alors que scan_forbidden fait grep -in LIGNE PAR LIGNE → l'escape doit être SUR la ligne fautive, une ligne voisine est inerte. 3e récurrence de la sous-classe `aucun git clean` mais 1re variante « marqueur mal placé » (les 2 précédentes = marqueur absent).

FIX (journal only, 0 prod, 0 artefact, 0 gate) : escape fusionné INLINE sur la ligne fautive + suppression du comment orphelin. Le journal-de-correction lui-même re-mord (il cite `git clean`) → escapé inline aussi. Vérif : grep -inE 'git +clean' filtré PROHIBITION+ci-allow = 0 hit résiduel ; ./run_ci.sh → 33 PASS · 0 FAIL · 0 SKIP (avec ce log inclus). Réflexe [[ci-gate-verification-method]] : re-jouer les gates sur le tree AVEC le nouveau log staged, pas seulement après l'édition de code.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 13:17:49 +00:00
Claude Code DTP Worker 127ef6f7fc [DTP-Worker 20260805_124241] FIX test-precision (surface neuve) : test_audit_4big.py figeait totals["pass"] = 18 - fail alors que le périmètre a grandi à 24 modules → le rapport « bad » de test_failing_module_forces_global_fail déclenchait INV7 (visé) MAIS AUSSI 2× INV8 (pass+fail=18≠24 modules ET min/max incohérents), contredisant son propre commentaire « rapport cohérent en interne ». Test relâché passant pour de mauvaises raisons + constante latente périmée. FIX (test only, 0 prod, 0 artefact rebuild) : totaux dérivés du vrai bad["modules"] (pass=len-fail, min/max=min/max(scores)) → rapport réellement cohérent, seul INV7 mord ; assertion resserrée any("< seuil") → all("INV7")+any("< seuil") prouve l'isolation. Preuve : rejeu de l'ancien 18 renvoie bien [INV7, INV8, INV8] et la nouvelle assertion échouerait dessus (dents réelles). 34 tests OK, 33 PASS CI. 0 gate ajouté (#5), 0 chiffre saisi (#6), 0 VPS (#8).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 12:49:55 +00:00
Claude Code DTP Worker 9b6ec60783 [DTP-Worker 20260805_121234] RÉ-ATTESTATION read-only du canal stakeholder : recompute python3 indépendant des 5 dimensions launch-readiness du daily_report 2026-08-05 (4Big 24/24@100 · régression 24 suites/624 ran/0-0-0 · recette 15/15 in_repo + 36 ci_job→0 orphelin vérifié BIDIRECTIONNEL vs les 33 vrais jobs gate.needs · structure 33=8 statiques+25 suites) → toutes CONCORDANTES, canal exact et courant. 0 fichier de prod édité, 0 gate ajouté (check_ci_integrity couvre déjà l'intégrité #5), 0 chiffre saisi à la main (#6), 0 commande VPS (#8). Restes roadmap S3/S5/S6 = refactor BIM/builds EAS/dépôts ONAPI/déploiement prod hors périmètre worker. 33 PASS CI. Seule édition : activity_log. 2026-08-05 12:16:44 +00:00
Claude Code DTP Worker a205fa64f2 [DTP-Worker 20260805_114227] AUDIT read-only roadmap↔repo (cross-refs numériques + liens) → CLEAN + clôture d'un faux-positif « 7 dashboards vs 6 »
Roadmap intégralement livrée/gatée, CI 33 PASS, daily_report du jour déjà écrit → audit ciblé de la classe peu balayée « cohérence chiffres/liens roadmap vs état réel du dépôt » (table Gains + Métriques MVP), via Explore + vérif manuelle.

Un seul candidat remonté, vérifié NON-défaut : la roadmap annonce « 7 dashboards uniformes » (L39/L83) alors que L36 énumère « clone /waf-home sur 5 entités ». Arithmétique naïve 1+5=6 → fausse : le 7e est wag-home, page-home DÉJÀ existante distincte de waf-home (attestée GAP_ANALYSIS_SPRINT1.md:91 « pages Frappe www/ (wag-home, waf-home, …) »). Donc wag-home + waf-home + 5 clones = 7 ; « 7 » sourcé et cohérent roadmap L39/L83 ET GAP L93 ; aucune occurrence littérale « 6 » → 0 contradiction interne. WAG (parent d'Helios RD, §Entités) a légitimement son dashboard. Dashboards = pages front VPS hors-repo, aucun générateur/artefact `dashboard` in-repo (seule livraison frontend gatée = portails/ 5 Workspaces rôle, classe distincte) → aucune source in-repo n'autorise un recompte. Éditer 7→6 inventerait une correction fausse (#6) + churn (#5) → LEAVE.

Reste CLEAN : RBAC 50 recompté=50, 5 portails, Expo 51 vs cible 54, 0 lien markdown cassé (03_agents/*/AGENT.md + 05_deliverables_mvp/*/README.md ; seuls non-résolus = chemins VPS + code-spans par design), 0 auto-contradiction ailleurs.

0 fichier de production édité, 0 gate ajouté, 0 chiffre inventé, 0 commande VPS, aucun git clean. Éditions : activity_log + mémoire de skip roadmap-7-dashboards-not-drift (pour que les futurs sweeps ne re-signalent pas). run_ci.sh 33 PASS/0 FAIL/0 SKIP.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 11:49:51 +00:00
Claude Code DTP Worker e0858f279b [DTP-Worker 20260805_111224] SWEEP read-only 3 classes de dérive doc → CLEAN + clôture d'une RÉCURRENCE de faux-positif de l'audit docstring-vs-code : le flag compat -o sur le sous-parser validate est un NON-défaut (bijection PARFAITE 8-vs-13 entre docstring validate [-o OUT] et argparse qui ajoute -o help="ignoré (compat)" — chaque module self-cohérent, zéro dérive). Décision LEAVE (anti-churn #5) : distinct de la convention de code de sortie harmonisée par 6b61ff8 (celle-ci avait une conséquence fonctionnelle main()->None vs 0 ; -o sur validate n'en a AUCUNE — flag ignoré, validate n'écrit pas), et hors OPEN_DECISIONS_REGISTER (réservé aux arbitrages produit de Michel). Classes 2+3 aussi CLEAN : 11 sous-comptes prose recomputés exacts, et classe NEUVE README/AGENT/docstring output-filenames ↔ out/ réel = 22/22 concordants (bancable excepté, pas d'out/ commité). 0 fichier de production édité, 0 gate ajouté, 0 chiffre inventé, 33 PASS CI. Mémoire docstring-vs-code-drift enrichie pour que les futurs sweeps Explore SKIPent le [-o OUT] (2e session qu'il est re-signalé à tort comme "7-8 défauts").
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 11:25:11 +00:00
Claude Code DTP Worker 6b61ff8b68 [DTP-Worker 20260805_104219] FIX de COHÉRENCE 4Big : acceptance_gen.py était le SEUL des 23 générateurs à rompre la convention universelle de code de sortie (main() -> None + appel nu main() vs -> int + raise SystemExit(main()) chez les 22 autres) → aligné (cmd_build/cmd_validate/main -> int, return 0, SystemExit(main())) ; le paramètre argv que tous exposent pour la testabilité renvoyait None au lieu de 0 à un appelant programmatique. Comportement inchangé (chemin d'échec _validate_or_die -> sys.exit(1) préservé, succès=0/échec=1), artefacts byte-identiques, 37 tests OK, 33 PASS CI. Classe docstring-vs-code auditée end-to-end aujourd'hui (23 gen + *lib/ helpers) = exacte par ailleurs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 10:49:58 +00:00
Claude Code DTP Worker 11e1cdd183 [DTP-Worker 20260805_101214] FIX RÉEL : la fiche MOBILE présentait oto_module_mobile_api.py comme source sans qualificatif alors que seul le .pyc existe — annotation « (compilé pyc) » perdue vs sa source citée AGENTS_EXISTING_ASSETS.md §8:59 (et convention sœur §9) → annotée pyc-only sur la seule ligne concernée, 4 sources réelles laissées intactes
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 10:24:25 +00:00
Claude Code DTP Worker fbe298ffd0 [DTP-Worker 20260805_094211] FIX RÉEL : le README module SEO annonçait « 36 tests (dont 8 injections négatives) » alors que la suite compte 10 méthodes test_negative_* (faux depuis le commit d'origine 0d3b242, sous-compte non gaté — le gate ne recompute que le total 36) → corrigé 8→10
Vérité-terrain : grep -c def test_negative_ = 10 (réparties Keywords/SchemaOrg/Hreflang/Manifest, PAS de classe dédiée) ; git show 0d3b242 = déjà 10 dès la création → jamais un état vrai antérieur (pas historical-repro). Piège confotur écarté : les 2 frères qui affichent un sous-compte négatif en prose sont EXACTS car adossés à une classe nommée countable (confotur « 8 négatifs » = classe TestNegative=8 ; devops « 14 injections négatives » = classe TestNegativeInjections=14) — SEO est le seul faux, ses négatifs ne forment pas de classe. Toute la classe « sous-compte négatif en prose » (seo/devops/confotur) auditée end-to-end.

Surfaces (balayage twin-fix) : seule seo/README.md:67 corrigée (présent-tense, doc vivante) ; les 2 autres occurrences du « 8 » sont dans daily_reports/2026-07-30-session18.md + 05_activity_log/2026-07-30.md = snapshots datés du 2026-07-30 → KEEP.

Effet de bord : audit_4big score le contenu README → quality_report.json régénéré EN DERNIER (seule ligne changée : evidence README.md 3900→3901 octets) · verdict PASS · 24/24 ≥95 (min 100) inchangé.

0 gate ajouté (#5 · erreur d'origine isolée, pas une dérive récurrente ; total déjà gaté ; précédent confotur = ne pas gater ces sous-comptes) · 0 édition d'autorité (le test file n'est pas modifié) · 0 chiffre inventé (#6) · 0 commande VPS (#8). run_ci.sh : 33 PASS

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 09:53:56 +00:00
Claude Code DTP Worker 5a96fc32d9 [DTP-Worker 20260805_091204] FIX de COUVERTURE doc : la fiche MOBILE omettait son propre livrable in-repo gaté crm→mobile/app_config (générateur de config app · 5 fichiers out · job mobile-app-config-tests dans gate.needs · CLI build|validate · 22 tests) — elle n'ancrait le rôle que sur RBAC + acceptance S5 (« deux artefacts in-repo »), jamais sur le générateur
Dernier des 22 modules livrés orphelin de fiche (grep -rl sur 03_agents/*/AGENT.md). Mirroir exact du pattern CRM 071201 : sous-section dédiée + 1 ligne de table AUTO-GATÉE par 3 gates indépendants de check_readme_claims (résolution par cible de lien mobile/app_config ∈ plan.suites) — cellule Tests 22==source · job CI mobile-app-config-tests==ci.yml · verbes CLI build|validate==subparsers. Pas de piège des 2 cadres de sprint : S5 dans les deux (acceptance evidence_modules S5 · roadmap ## SPRINT 5 ligne Mobile L56) → cité L56. 3 énumérations « ancrage in-repo » réconciliées (sinon contradiction avec la nouvelle section), toutes sourcées. 0 out/ régénéré (git status = fiche seule) · 0 gate ajouté (#5) · 0 édition d'autorité · 0 chiffre inventé (#6, 22 lu dans regression_plan) · CI 33 PASS.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 09:22:50 +00:00
Claude Code DTP Worker d7dbda9e2b [DTP-Worker 20260805_084202] FIX RÉEL : « 6 sections » périmé dans les 2 docs d'IMPLÉMENTATION de crm/financement_bancaire alors que le module en livre 7
Classe prose FACT count ungated (prose-facts-vs-numeric-drift). Défaut : README:9 « la structure des 6 sections » + fiche CRM AGENT.md:47 « cœur du gate + 6 sections » décrivaient le livrable avec un compte périmé. Le bon compte = 7 est triple-sourcé et byte-gaté : out/MANIFEST.json counts.sections=7 · financement_spec.json bloc sections=7 (apport_initial,info_achat,choix_banque,formulaires,exigences,autorisations,envoi) · et le README se contredisait lui-même (L49 énumère 7, L57 « 7 sections » annotée source=MANIFEST). Origine du 6 : la DIRECTIVE originale énumérait 6 sections non-gating puis fut amendée pour ajouter apport_initial (gating, pos.1) → 7 ; spec+module ont suivi, les 2 prose L9/L47 non.

Correctif 6→7 sur les 2 SEULES surfaces d'implémentation. Les 4 « 6 sections » restantes sont des snapshots directive gelés (DIRECTIVE_FINANCEMENT L106/122/131 + DIRECTIVE_WORKFLOW_V10 L22/47) — input-specs datés de Michel, jamais réécrits (directive-vs-implementation · spec=authority) ; leur « 6 » est exact au cadre pré-amendement → INTACTS.

0 gate ajouté (#5 · un gate structuré ne mordrait pas la prose L9, hors-cible ; SIGNAL surfacé : financement seul module dont MANIFEST.counts n'est pas recompté par check_readme_claims). 0 chiffre inventé (#6). 0 production éditée. 0 commande VPS (#8). run_ci.sh 33 PASS 0 FAIL 0 SKIP.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 08:50:13 +00:00
Claude Code DTP Worker d6cc09da0d [DTP-Worker 20260805_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 manuscrites (README:3 prose + gen.py:4 docstring) · clôt le SIGNAL surfacé non tranché par la session 071201
- Finding : ligne 3 du README « Sprint 5 · roadmap L52 (workflow vente end-to-end · volet financement) » = citation roadmap-GLOBALE (cite L52 + reprend le texte du deliverable S4), PAS un cadre agent-interne → tranchée fausse par 4 autorités concordantes :
  · fichier roadmap : ## SPRINT 4 = L48, deliverable L52 ; ## SPRINT 5 = L54 « ONAPI + Compliance + Mobile » (rien à voir) → « Sprint 5 · roadmap L52 » auto-contradictoire
  · autorité byte-gatée acceptance_spec.json (partition INV5) : crm/financement_bancaire ∈ S4 · roadmap_line 52
  · 3 modules frères du bucket S4 tous « Sprint 4 » (workflow_vente/dossier_vente/commissions · L51-52) → financement seul outlier
  · fiche 03_agents/crm/AGENT.md liste déjà « 4 (roadmap L52) » → le module contredisait sa propre fiche
- Provenance surfacée non éditée : DIRECTIVE Michel :140 cadre « [Sprint 5 · Financement] » (son snapshot d'entrée) transcrit à tort dans un slot roadmap-global ; directive laissée INTACTE (input immuable, comme daily_reports)
- Balayage exhaustif : exactement 2 surfaces manuscrites (README:3 + gen.py:4) ; aucun spec/artefact/out/gate ne porte de champ sprint → build ne régénère aucun artefact (check_artifacts vert)
- 0 gate ajouté (#5 · label sprint = piège à 2 cadres légitimes, gate aveugle = faux positifs sur frames agent-internes) · 0 édition d'autorité (acceptance disait déjà S4) · 0 chiffre inventé (#6) · 0 commande VPS (#8) · CI 33 PASS

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 08:16:29 +00:00
Claude Code DTP Worker a39dc28f59 [DTP-Worker 20260805_074202] FIX de COUVERTURE : le livrable transverse demo/scenarios (Sprint 7 · CRM+Faisabilité) manquait des DEUX fiches co-porteuses → recensé, une seule source gatée
Complétude≠exactitude (même classe que le fix CRM/financement précédent). Diff
05_deliverables_mvp/*/ vs tables des 13 fiches : demo/scenarios est un livrable
produit (générateur demo_scenario_gen.py · 39 tests · out/{run_sheet,MANIFEST} ·
README déjà gaté · job CI demo-scenario-tests) mais recensé dans AUCUNE fiche
(grep demo|scenario sur 03_agents/*/AGENT.md = 0). Livrable transverse SANS agent
propre : README « Sprint 7 (CRM+Faisabilité) » + roadmap L68 l'assignent aux DEUX.

- Fiche CRM : sous-section « Livrable transverse · Sprint 7 » + table 1-ligne
  AUTO-GATÉE (label scenarios/ · chemin demo/scenarios ∈ plan.suites · cellule
  Tests recomputée depuis regression_plan.json par le row_re générique de
  check_readme_claims — la ligne HÉRITE du gate, 0 gate ajouté #5). Framée honnête :
  PAS un livrable CRM propre mais méta-générateur qui COMPOSE l'amont, source
  distincte du trio + du financement (laissés INTACTS).
- Fiche Faisabilité : cross-réf en prose (0 chiffre redupliqué #5) vers la fiche CRM
  + README module ; bancable alimente S-P07-BANQUIER.

Auto-gates franchis (3 axes) : Tests 39==source(39) · verbes build|validate==
subparsers · citation roadmap l.68==roadmap · job CI==ci.yml · check_docs cross-refs OK.
2 fichiers touchés + log. 0 chiffre inventé (#6) · 0 production éditée · 0 gate ajouté
(#5) · 0 commande VPS (#8). CI 33 PASS · 0 FAIL · 0 SKIP.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 07:50:35 +00:00
Claude Code DTP Worker f73404a6a4 [DTP-Worker 20260805_071201] FIX de COUVERTURE doc : la fiche CRM omettait son 4e module livré crm/financement_bancaire (absent des 13 fiches agents) → sous-section dédiée + ligne de table AUTO-GATÉE par 3 gates indépendants de check_readme_claims (cellule Tests 35==source · job CI crm-financement-bancaire-tests==ci.yml · verbes CLI build|validate==subparsers, résolution par cible de lien crm/financement_bancaire∈auth) · trio pipeline + agrégat gaté « Total CRM 81 tests » laissés INTACTS (financement ne partage pas la source unique workflow_vente_spec → hors-trio, sinon faux sémantiquement + gate cassé) · Sprint 4·L52 lu dans acceptance_spec.json (autorité roadmap-globale, PAS le Sprint 5 agent-interne du README module) · 35 tests lu dans regression_plan · SIGNAL surfacé non tranché : README module « Sprint 5 » vs S4 acceptance (piège des 2 cadres de sprint, édition spec+regen coordonnée requise) · 0 édition d'autorité · 0 gate ajouté (#5) · 0 chiffre inventé (#6) · CI 33 PASS
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 07:23:07 +00:00
Claude Code DTP Worker caa3b90556 [DTP-Worker 20260805_064201] FIX RÉEL : fenêtre de sprint roadmap-globale de faisabilite/generator corrigée S2→S3
Défaut (1re vraie édition de prod après plusieurs sessions de sondes « 0 édition ») :
quality_spec.json:66 attribuait au générateur de faisabilité 4 volets la fenêtre roadmap
« S2 » avec un source falsifiable-faux (« roadmap Sprint 2 · Faisabilité… »), alors que la
roadmap n'a AUCUNE faisabilité au Sprint 2 (Console/CRM/RBAC) et place « génération 4 volets »
au Sprint 3 (L43, livrable L46 « P07 faisabilité complète auto-générée »). Cause racine :
confusion de 2 cadres de sprint — le label agent-interne « S2 » (AGENT.md §130-133 : phases
S1 template/S2 générateur/S3 versioning, toutes ancrées roadmap Sprint 3) recopié dans le
champ roadmap-global (seul quality_spec le porte, toutes ses entrées sourcent « roadmap
Sprint N »). Effet : le livrable-phare S3 était prouvé SANS son propre générateur, ce dernier
mal-classé comme preuve du S2 (dashboards/CRM/RBAC) sans rapport.

Correctif coordonné 2-spec + régénération byte-gatée :
- quality_spec.json:66 : sprint S2→S3 + source recitée « roadmap Sprint 3 » (style aligné sur
  le jumeau bancable, déjà S3).
- acceptance_spec.json : generator retiré de evidence_modules S2, ajouté à S3
  (sinon invariant 5 « partition exacte » casse : want lu dans quality_spec ≠ have déclaré).
- régénération audit_4big PUIS acceptance (lit la fenêtre depuis quality_spec).

Vérif : partition_ok=True · bijective=True · S2=6 (publiciste+5 rbac) · S3={bancable,generator}
· run_ci 33 PASS · score 4Big générateur inchangé 100/100 (le champ sprint ne sert qu'au tri+méta,
pas d'oracle de scoring). Portée honnête : 5 fichiers (2 specs + 3 artefacts régénérés) + log.
0 doc de module éditée (les ~10 « S2 » ailleurs = cadre agent-interne, corrects, #5) · daily_reports
= historique non réécrit · 0 gate ajouté (déjà gaté par inv5) · 0 chiffre inventé (#6) · 0 VPS (#8).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 06:56:32 +00:00
Claude Code DTP Worker 40ddab05af [DTP-Worker 20260805_061201] Sweep REPO-WIDE de la classe « chemin fantôme en code-span backtick » (ungated) : étend l'audit d'exactitude des 13 fiches à TOUT le markdown suivi (~200 .md · READMEs, daily_reports, roadmap, ci/README, DIRECTIVE_*, logs) → résolution multi-ancre (doc-dir · repo-root · deliverables-root · sous-modules) → 0 fantôme réel · les ~180 hits = 3 formes bénignes vérifiées (runtime/VPS hors-dépôt · citations cross-module en prose dont le backtick est le libellé d'un vrai lien validé par check_docs · narratif d'historique + non-chemins pédagogiques ./inexistant + /tmp/suite_teeth harnais) · 2 axes de câblage CI re-confirmés structurellement clos (run_ci dérive gate.needs · check_ci_integrity INV-B attrape un ci/*.sh orphelin) · 0 défaut réel → 0 édition de production (#5) · 0 gate ajouté (backticks volontairement neutralisés + gate lisant /opt/oto non-portable côté runner Gitea) · 0 chiffre inventé (#6) · aucune commande VPS (#8) · CI 33 PASS
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 06:18:30 +00:00
Claude Code DTP Worker b8447363ff [DTP-Worker 20260805_054200] Currency canal STAKEHOLDER : consigner la clôture des DEUX axes de « dents » dans daily_reports/2026-08-05.md
Le rapport stakeholder du jour (session 004112) s'arrêtait au jalon 33 jobs et
PRÉCÉDAIT toute la série d'attestation teeth des suites de MODULE (BATCH 1→4,
034144→051154, clôture 25/25). Le canal daily_reports ne captait donc aucun des
deux axes de « dents ». Section « fin de journée » ajoutée :
  · axe A — 8/8 gates statiques mutation-prouvés (9fe8bf1/031134)
  · axe B — 25/25 suites de module mutation-prouvées (6+6+6+7)
Chaque chiffre sourcé sur une structure commitée (ls ci/*.sh=8 · gate.needs=25 ·
série de commits) — 0 saisi à la main (#6). Message : les tables prouvent la CI
VERTE, les attestations teeth prouvent qu'elle MORD (gate/suite toujours-vert =
faux vert invisible).

Avant de choisir : chasse docstring↔code drift sur les 21 générateurs (classe
récurrente) → 0 contradiction réelle ; les « docstrings sans liste-sorties »
sont des faux positifs (sorties documentées dans Sous-commandes), uniformiser =
churn écartée (#5).

0 édition de production · 0 gate ajouté (#5) · aucune commande VPS (#8) ·
aucun git clean · CI 33 PASS.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 05:49:50 +00:00
Claude Code DTP Worker 986d29362c [DTP-Worker 20260805_051154] Attestation des DENTS des suites de MODULE — BATCH 4 (FINAL) : mutation-test les 7 dernières suites → CLÔTURE 25/25 · 7 BITES · 0 TOOTHLESS
6 mutations de LOGIQUE de générateur (sortie re-assertée en mémoire) : portails roles_couverts sum +1 · audit-4big totals.fail len(below) +1 · demo duree_min sum +1 · regression test_methods sum -1 · deploy couverture-bijective gated_set-mapped_set → | · acceptance exact want==have → !=. 2 mutations proposées par Explore étaient INERTES (and→or sur 'not [] and not []' reste True) — remplacées par des mutations qui font BASCULER la sortie observée (leçon (b) désambiguïsation test-édenté vs mutation-inerte appliquée en direct).

1 sous-classe DISTINCTE : rbac-aggregate (test_rbac.py) n'a pas de builder/out — il valide le CONTRAT rbac_50_roles.json (schéma, ==50, unicité, 5 portails, set_user_permissions #6), structurellement non-echo ; dent prouvée par dérive du contrat cible 50→49 (test_exactement_50_roles). Axe contrat signalé honnêtement ≠ axe logique-générateur.

Clôture 25/25 des suites de MODULE mordantes + 8/8 gates STATIQUES = Sprint 8 'regression tests exhaustifs' attesté sur ses deux axes de DENTS. Harnais /tmp read-only (baseline verte → cp → str.replace unicité → run suite seule → assert exit≠0 → restore cp+cmp), trap EXIT, jamais git clean/checkout, arbre byte-pristine. 0 gate ajouté (#5) · 0 chiffre inventé (#6) · 0 édition prod · aucune commande VPS (#8) · CI 33 PASS

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 05:19:04 +00:00
Claude Code DTP Worker e38dd933c6 [DTP-Worker 20260805_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
Continuation de l'axe des DENTS des suites de MODULE ([[module-suite-teeth-mutation]]) : batches 034144+041149 avaient prouvé 12/25 ; ce batch 3 en couvre 6 de plus → 18/25 · 7 restantes non-couvertes ≠ édentées (pas de plafond silencieux).

6 BITES (chaque suite mord sur une dérive de LOGIQUE de son générateur, jamais un écho d'artefact) :
- rbac/fixtures_gen builder:43 if_owner ==→!= (test_if_owner_reflete_scope_own)
- rbac/userperm_gen builder:43 by_mechanism += 1→+=2 (sum==50)
- rbac/apply_plan aggregator:174 chaîne bijective 2e ==→!= (assertTrue couverture_bijective)
- mobile/app_config builder:105 roles=sorted(...)+reverse=True (==sorted(roles_map[portail]))
- qa/audit_5d builder:60 count(_FAIL)→count(_PASS) (totals fail:0→13)
- frontend/chat_otoia builder:114 sum(nb_roles)→sum(1) (test_44_roles_couverts 44→~5)

Axe distinct des DENTS des 8 gates STATIQUES (031134, 8/8) : une suite qui rechargerait out/*.json contre elle-même serait édentée = faux vert Sprint 8 · les suites reconstruisent en mémoire depuis le contrat.

LEÇON réappliquée (désambiguïser TOOTHLESS-test vs mutation INERTE) : le 1er pick mobile (:192 roles_couverts sum→len) était édenté pour la suite de MODULE (agrégat jamais ré-asserté en mémoire) ; basculé sur :105 (tri, réellement contraint). Idem fixtures :68 → :43.

Harnais /tmp read-only, remplacement d'octets littéral (assert 1 occurrence), run de la seule suite, restore byte-exact cp+cmp, trap EXIT, jamais git clean, arbre pristine · 0 gate ajouté (#5) · 0 chiffre inventé (#6) · 0 édition prod · CI 33 PASS

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 04:48:56 +00:00
Claude Code DTP Worker d25da28d9c [DTP-Worker 20260805_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
Continuation honnête de l'axe DENTS-des-suites (034144 : 6/25, 19 non-couvertes≠édentées). Batch 2 couvre 6 de plus → 12/25. Chaque suite doit sortir NONZERO sur une dérive de LOGIQUE de son générateur (jamais un écho d'artefact) : scorer completude 20*filled, bancable point-équilibre math.ceil, workflow-vente tri-déterminisme .sort, dossier is_submittable seuil, financement apport /100.0, publiciste _fmt_usd séparateur espace.

LEÇON : un verdict TOOTHLESS doit être désambiguïsé — (a) test réellement édenté vs (b) mutation comportementalement INERTE pour la donnée du contrat. 2 mutations ont d'abord lu TOOTHLESS mais étaient (b), PAS des défauts : dossier >= '1'→>'1' inerte car max doc_status='2' (les 2 branches donnent 1) → une mutation qui FAIT BASCULER le résultat (>= '3') mord ; publiciste min→max inerte car _prix_depuis n'alimente que la gate is-not-None, sa valeur min n'est jamais affichée (obs. honnête, pas un défaut #5/#6) → on mute une logique observée (_fmt_usd). Règle : confirmer que la mutation change la SORTIE pour la fixture avant de croire un TOOTHLESS.

Axe distinct des DENTS des 8 gates STATIQUES (031134, 8/8). Portée honnête : 13 restantes non-couvertes≠édentées (pas de plafond silencieux). Harnais /tmp read-only, restore byte-exact cp+cmp, trap EXIT, jamais git clean, arbre pristine. 0 gate ajouté (#5) · 0 chiffre inventé (#6) · 0 édition prod · CI 33 PASS

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 04:21:01 +00:00
Claude Code DTP Worker 98f3f56c3a [DTP-Worker 20260805_034144] Attestation des DENTS des suites de test de MODULE : mutation-test generator-logic sur 6 des 25 suites unittest (chacune doit sortir NONZERO sur une dérive de LOGIQUE de son générateur — commission base*taux, _statut gated/a_construire, e-NCF zfill(10), nb_roles, dédup SEO, séparateur options confotur — jamais un écho d'artefact) → 6 BITES · 0 TOOTHLESS · axe distinct des DENTS des 8 gates STATIQUES (031134, 8/8) : une suite qui rechargerait out/*.json contre lui-même serait édentée = faux vert Sprint 8 comme un gate édenté · les suites reconstruisent en mémoire depuis le contrat, prouvé qu'elles contraignent la LOGIQUE · portée honnête = 6/25 échantillonnées pour diversité, 19 restantes non-couvertes≠édentées (pas de plafond silencieux) · harnais /tmp read-only, restore byte-exact cp+cmp, jamais git clean, arbre pristine · 0 gate ajouté (#5) · 0 chiffre inventé (#6) · 0 édition prod · CI 33 PASS
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 03:50:47 +00:00
Claude Code DTP Worker 9fe8bf1feb [DTP-Worker 20260805_031134] Attestation launch-readiness des DENTS du gate : mutation-test de suite complète des 8 gates statiques (chacun doit sortir NONZERO sur sa dérive dédiée) → 8 BITES · 0 TOOTHLESS · aucun édenté malgré +1 gate (8ᵉ check-mobile-workflow) et ~3j d'éditions depuis la dernière suite-mutation (7/7 le 08-02) · axe distinct des attestations de CONTENU (run_ci prouve PASS, pas FAIL — un gate toujours-vert = faux vert launch-readiness) · harnais /tmp read-only, restore byte-exact par cp (jamais git clean), arbre pristine · 0 gate ajouté (#5) · 0 chiffre inventé (#6) · 0 édition prod · CI 33 PASS
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 03:18:28 +00:00
Claude Code DTP Worker c2c72dc7a8 [DTP-Worker 20260805_024134] Attestation launch-readiness OPEN_DECISIONS_REGISTER (18 citations file:line + 2 numériques d'en-tête re-prouvées exactes) + 1 FIX de dérive latente : dé-figeage des 2 compteurs mobiles gelés dans le doc (33 gates · 24 modules 100/100) → reformulés qualitatifs + pointeur vers sources dérivées-et-gatées (README ## État courant + quality_report.json), alignés sur la philosophie no-hardcoded-count du README (32→33 il y a 2 jours, 24 déjà gaté) · 0 gate ajouté (#5) · 0 édition d'autorité (SURFACE-don't-rewrite) · 0 chiffre inventé (#6, on en retire 2) · CI 33 PASS
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 02:47:35 +00:00
Claude Code DTP Worker 30e4af8a49 [DTP-Worker 20260805_021124] OPEN_DECISIONS_REGISTER rendu découvrable : indexé dans Navigation README (à côté de son sibling GAP_ANALYSIS) + pointeur « État courant » (CI verte ≠ zéro décision en attente) → le punch-list launch-readiness n'était référencé nulle part sauf 1 ligne de journal · 3 claims du registre re-prouvés avant promotion (#6) · 0 édition d'autorité (SURFACE-don't-rewrite) · 0 gate (#5) · CI 33 PASS
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 02:14:31 +00:00
Claude Code DTP Worker 7e4affe9d8 [DTP-Worker 20260805_014119] Registre des décisions ouvertes (launch-readiness Sprint 8) : consolidation sourcée des arbitrages produit/dépendances hors-dépôt éparpillés dans les logs → 4 items OUVERTS re-prouvés indépendamment (PIE contrats→confotur over-attribution · financement WAG→audit IA · chat.py+projets_editor.py absents déjà surfacés 133718) + 1 soupçon INVALIDÉ (bundle id mobile, VÉRIFIÉ-SANS-SUITE) · 0 édition d'autorité (#6/SURFACE-don't-rewrite) · CI 33 PASS
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 01:47:04 +00:00
Claude Code DTP Worker 84df56ae2d [DTP-Worker 20260805_011114] Double sweep read-only des 2 classes de dérive free-text ungated (docstring↔code des 22 générateurs · inventaire↔filesystem /opt/oto) → CLEAN · 0 édition prod · faux nouveau-finding projets_editor/chat.py écarté (déjà surfacés 2026-08-03, anti-invention #6) · 0 gate ajouté (#5) 2026-08-05 01:17:45 +00:00
Claude Code DTP Worker 3d6c2b7d4f [DTP-Worker 20260805_004112] Canal stakeholder daily_reports au jalon 33 jobs (32→33 après 8e gate check-mobile-workflow) + réattestation python3 indépendante des 4 chiffres → CLEAN · 0 édition prod
- daily_reports/2026-08-05.md : snapshot roadmap-facing consignant le cap 33 jobs
  (= 8 gates statiques + 25 suites) ; le canal s'arrêtait au 08-04 (« 32 PASS ») alors
  qu'un changement structurel de CI a atterri le 08-05 (session 001109).
- Chaque figure recomputée python3 vs artefact commité (aucune saisie main · #6) :
  quality 24/24@100 · regression 24 suites/624 · acceptance 15/15+36 ci_job · struct 33=8+25.
- Vérif préalable : aucun décompte de jobs présent-tense figé en dur (dérivé run_ci --list) ;
  les « 32 » antérieurs = snapshots datés (historical-repro = KEEP), pas de dérive.
- run_ci 33 PASS · check_docs/guard verts · 0 édition prod · 0 commande VPS (#8).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 00:45:43 +00:00
Claude Code DTP Worker b7ffb6448f [DTP-Worker 20260805_001109] Nouveau gate check-mobile-workflow : la structure de mobile-build.yml devient opposable au merge (surface mutation-testée UNGATED, 1re couverture) → 32→33 jobs
- ci/check_mobile_workflow.sh (8e gate statique) · 4 invariants lus dans le fichier (#6) :
  MOB-1 bien-formé (YAML strict si PyYAML) · MOB-2 aucun secrets. en if: de job
  (portabilité act/Gitea, verrouille le correctif 234104) · MOB-3 builds gatés sur
  needs.preflight.outputs.has_token (activation différée #6/#8) · MOB-4 contrat outputs
  (déclarés==référencés==alimentés, 0 dangling).
- Preuve UNGATED avant ajout : réintroduire secrets. en if: de job → 7/7 gates statiques verts.
- Gate mutation-testé : 5 mutations toutes ROUGES, fichier propre re-VERT.
- Câblé dans ci.yml + gate.needs ; check-ci-integrity INV-A/B verts ; run_ci auto → 33 PASS.
- ci/README.md : ligne §1 + détail §2 + nuance « Second workflow ».
- 0 nouveau module · 0 chiffre inventé · 0 edition prod hors CI · aucune commande VPS.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 00:21:58 +00:00
Claude Code DTP Worker c5c48dff53 [DTP-Worker 20260804_234104] Durcissement mobile-build.yml : gating d'activation via preflight-outputs (secrets-context hors if: job → risque SKIP silencieux) → 1 édition CI in-scope
- build-ios/build-android gataient sur `if: secrets.EAS_TOKEN != '' …` au niveau JOB,
  où le `secrets` context n'est pas garanti (GitHub Actions ne l'expose pas dans
  jobs.<id>.if ; act/Gitea = ambigu) → les builds pouvaient rester SKIP en silence
  le jour où Michel fournit un EAS_TOKEN valide.
- Correctif = pattern canonique preflight-output : secret lu dans un step de preflight
  (secrets y est toujours dispo) → outputs booléens has_token/has_repo → gate consommé
  via needs.preflight.outputs.* (dispo en if: partout · portable GitHub ET Gitea/act).
- Contrat observable inchangé (preflight vert + rapporte les secrets · builds SKIP sans
  token) → prose ci/README.md §Second workflow toujours exacte, 0 dérive doc.
- Vérif : YAML valide · 0 secrets. restant dans un if: de job · needs:preflight conservé ·
  4 écritures $GITHUB_OUTPUT · aucun gate ne linte le workflow (hors gate.needs) ·
  run_ci.sh 32 PASS inchangé.
- 2 canaux de log à jour (activity_log + daily_reports addendum). 0 nouveau module (#5) ·
  0 chiffre inventé (#6) · aucune commande VPS (#8).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-04 23:51:40 +00:00
Claude Code DTP Worker 8a10d478f3 [DTP-Worker 20260804_231101] Attestation indépendante des 4 chiffres stakeholder (recompute python3 vs artefacts commités) → CLEAN · 0 édition prod
- Recompute indépendant (charge JSON, recompte · #6) : qualité 24/24@100 + coverage ci==registry==24 · régression 24 suites/624 ran/passed · recette 15/15 in_repo + ci_job ×36 (niché dans matrix[].modules[]) · run_ci 32 PASS → tous concordent byte-pour-chiffre avec la table publiée (pas d'artefact-echo)
- Faux-négatif écarté : ci_job niché dans modules[], pas au niveau ligne (parse naïf rend 0)
- Confirmé : regression_run.json (exclu de check_artifacts) est protégé par check_regression.sh (run frais == commité byte-for-byte) → totaux 624 non-périmables en silence
- 2 canaux de log à jour (2e addendum daily_reports 08-04 + activity_log)
- 0 nouveau module · 0 gate ajouté (#5) · 0 chiffre inventé (#6) · aucune commande VPS (#8)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-04 23:16:09 +00:00
Claude Code DTP Worker 89a03982d6 [DTP-Worker 20260804_224101] Sonde dérive NON gatée (4 dimensions neuves : cross-refs fiches · dates futures · contacts escalade · specs↔dossiers) → CLEAN · 0 édition prod · faux-positif email écarté (occurrence unique ≠ drift, anti-invention #6) · 2 canaux de log à jour · run_ci 32 PASS
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-04 22:46:30 +00:00
Claude Code DTP Worker 81484927ce [DTP-Worker 20260804_221055] Passe vérif intégrité inter-artefacts (5 surfaces, 3 NEUVES au compte courant 24/32) → CLEAN 0 édition prod + ouverture canal daily_reports 08-04
Surfaces auditées, toutes CLEAN :
1. docstring-vs-CODE / comptes sortie (~25 gen, Explore) → 0 mismatch présent-tense
2. correctness commissions (NEUF) → taux_pct=null by design (#6), aucune réconciliation matérialisée ne peut dériver
3. réconciliation compte au point 24 (NEUF) → quality 24 · coverage ci==registry==24 · regression 24/624 · acceptance 15/15 · gate.needs 32, tous concordants
4. ci_job recette→gate.needs au 24/32 (NEUF, prolonge sonde 000623 faite à 22/30) → 36 refs 0 orphelin, 2 nouveaux modules (financement/pie) présents, 8 non-cités = infra CI (by design)
5. couverture ci/README (NEUF) → §1 = table 7 gates statiques by design, suites module dérivées de gate.needs à l'exécution + gatées par check-ci-integrity INV-A → pas de gap

Aucune tâche fonctionnelle in-repo restante (marqueurs à-créer = DocTypes VPS #8 ; mobile-build.yml déjà fait ; Publiciste existe). Canal 2 08-04 ouvert, snapshot 100% sourcé recompute python3 indépendant. run_ci 32 PASS · 0 gate ajouté (#5) · 0 chiffre inventé (#6) · 0 commande VPS (#8)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-04 22:19:39 +00:00
Claude Code DTP Worker 503f726ae7 [DTP-Worker 20260803_200738] Re-vérif indépendante couple PLANPOINT↔spec (2 contradictions confirmées RÉELLES) + rattrapage canal daily_reports (3 sessions) · 0 édition prod
Post-fermeture du cycle « audit 8 directives » (88bd025), plutôt qu'un 9e audit
redondant : re-vérification indépendante de la dernière édition substantielle
(encart Arbitrage inséré dans specs/CHOISIR_MON_UNITE_SPEC par a9e9ee9), point le
plus susceptible d'abriter un défaut résiduel.

VÉRIF · les 2 contradictions de l'encart sont CONFIRMÉES RÉELLES (grep croisé
directive↔spec) :
 · Décision 4 Signature : spec=DocuSign (SaaS externe, l.58/89/112) ↔ directive §7
   l.53=OTO Sign™ (natif)
 · Décision 5 Comparateur : spec=NON (l.59/90/119) ↔ directive §6 l.48/86=max 3 unités
→ aucun faux-positif inséré · édition a9e9ee9 factuellement saine · 0 correction.

SIGNAL Michel ENRICHI (nouvel angle, non tranché) · la Décision 4 n'est pas neutre
vis-à-vis du mandat : CLAUDE.md #1 = « ERPNext natif priorité absolue avant tout
outil externe » → OTO Sign™ (directive/natif) plus conforme que DocuSign (spec/SaaS
externe) ; directive aussi la + récente (08-03 vs 07-28) → récence ET conformité #1
pointent vers OTO Sign™. NON flippé (spec=autorité, #5).

RÉALISÉ · rattrapage daily_reports/2026-08-03.md (s'était arrêté à 173726, 3 sessions
de retard : 180728/190730/193734/200738). Chiffres 100% re-dérivés d'artefacts
committés : 24/24 qualité (quality_report.coverage) · 624/624 régression
(regression_run.totals) · 15/15 recette (acceptance_matrix) · 32 gate (run_ci).
Canal gate-neutre (hors out/ byte-gaté, hors audit_4big/check_artifacts), même
posture que 063711.

run_ci 32 PASS · 0 FAIL · 0 SKIP inchangé · 0 fichier prod touché · 0 commande VPS (#8).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-03 20:12:46 +00:00
Claude Code DTP Worker 88bd0256b7 [DTP-Worker 20260803_193734] README index honnête sur le couple PLANPOINT→spec + fermeture audit des 8 directives Michel
ÉDITION DOC RÉELLE · README §Directives ligne PLANPOINT_STYLE : ajoute la flèche
→ specs/CHOISIR_MON_UNITE_SPEC (jusqu'ici la directive n'y était décrite que comme
« exigence UI », sans nommer la spec qu'elle pilote — alors que la session a9e9ee9
avait déjà ajouté à la spec un encart Arbitrage pointant VERS la directive). Le lien
directive→spec manquait à l'index : asymétrie corrigée, couple audité désormais
découvrable depuis l'index. Mention des 2 arbitrages rouverts (signature DocuSign↔
OTO Sign™ · comparateur) non tranchés (spec = autorité) + raison du classement
bucket-2 (spec non byte-gatée).

Fermeture · les 3 directives restantes non encore auditées (ARCHIVES_DEBLOCAGE ·
RENDUS_EXISTANTS · UNBLOCK_NOW) = snapshots runtime/VPS hors périmètre worker (#8),
correctement classées bucket-2, toutes indexées (8/8). Marques RENDUS 7/7 conformes
à pie_manifest.json (couche marque ≠ code-name §Projets, cf. mémoire) → CLEAN.
Audit des 8 directives Michel désormais complet.

Gate-safety · nouveau lien résout (check_docs vert) · « 2 arbitrages » = prose
fidèle à l'encart spec, pas un compteur gaté · check_readme_claims + guard_constraints
verts · run_ci 32 PASS / 0 FAIL. Aucune décision produit flippée.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-03 19:41:35 +00:00
Claude Code DTP Worker a9e9ee9f3c [DTP-Worker 20260803_190730] Audit directive-vs-SPEC couple NEUF (DIRECTIVE_PLANPOINT_STYLE 08-03 ↔ specs/CHOISIR_MON_UNITE_SPEC 07-28) → édition doc RÉELLE : encart Arbitrage rouvert surfaçant 2 contradictions dures (#4 Signature DocuSign↔OTO Sign™ · #5 Comparateur NON↔max 3) + précision acompte 20%↔20/30% Ley126-02 · SANS flipper aucune décision (spec=autorité, directive=snapshot daté) · spec rendue honnête (ne prétend plus tout-tranché alors qu'un input Michel postérieur rouvre 2 points) · gate-neutre (spec hors audit_4big/ci/check_artifacts) · 3 sondes préalables CLEAN (docstring-vs-code 22 gen · réconciliation 24/32 · daily_reports série temporelle) · SIGNAL Michel 2 arbitrages produit · run_ci 32 PASS
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-03 19:17:41 +00:00
Claude Code DTP Worker e6b89435fd [DTP-Worker 20260803_183728] README indexe la classe d'artefacts DIRECTIVE_*.md → édition doc RÉELLE (pas signal-only)
Sondes préalables 0 dérive : (1) docstring-vs-CODE 22 générateurs + helpers = fidèles aux fichiers réellement écrits/clés lues, aucun phantom ; (2) 4 directives non-auditées (ARCHIVES_DEBLOCAGE/PLANPOINT_STYLE/RENDUS_EXISTANTS/UNBLOCK_NOW) = opérationnelles/inventaire/UI/autorisation, PAS des couples directive→module byte-gaté, rien à gater.

Gap RÉEL : les 8 DIRECTIVE_*.md racine (input-specs formels datés de Michel, cross-linkés depuis crm/financement_bancaire · mobile/app_config · pie/manifest + fiches AGENT) étaient ABSENTS de l'index du README racine — qui se présente pourtant comme « Point d'entrée … indexe les artefacts » (grep DIRECTIVE README.md = 0). Classe entière invisible depuis le point d'entrée.

Édition README.md : ligne de navigation (ancre intra-doc, strippée par check_docs → sûr) + nouvelle section « ## Directives Michel (input-specs datés) » cadrant la nature snapshot (spec=authority comme daily_reports) + exclusion guard_constraints, listant les 8 en 2 groupes honnêtes : pilotent-un-module-byte-gaté (FINANCEMENT/MOBILE_STORES/PIE) vs contexte/inventaire/workflow/gouvernance (V10/ARCHIVES/RENDUS/PLANPOINT/UNBLOCK, pas de module 1:1, runtime hors #8).

Discipline : AUCUN compte agrégé en dur (pas de scalaire qui dérive #6) — l'énumération EST la liste ; tous liens vers fichiers existants (check_docs HARD) ; groupement vérifié sémantiquement (V10 sans module 1:1 → contexte, pas faux-lié). 0 module créé · 0 gate ajouté (#5) · 0 chiffre inventé (#6) · 0 commande VPS (#8).

run_ci.sh --static 7/7 PASS (check-docs liens OK + check-readme-claims vert). Baseline complet 32 PASS.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-03 18:46:32 +00:00
Claude Code DTP Worker 86fbc87a56 [DTP-Worker 20260803_180728] Log session · audit directive-vs-implémentation 4e couple (DIRECTIVE_PIE → pie/manifest) → CLEAN 0 édition prod · byte-repro + 10 invariants verts · fidélité directive→spec totale (12 groupes/4 sync/10 étapes/9 marques/annexe12/V10.1) · attributions module vérifiées SÉMANTIQUEMENT (au-delà inv6 dir-existence) : 3 exactes (site→publiciste/kit→bancable/app→mobile) · SIGNAL Michel : downstream contrats gated→legal/confotur MAIS confotur=DocType CONFOTUR Ley158-01 ≠ Promesa/Fideicomiso/HOA (0 module ne les génère) → couverture sur-affirmée = arbitrage périmètre produit, pas édition (spec=authority, #5 no gate) · run_ci 32 PASS
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-03 18:12:48 +00:00
Claude Code DTP Worker 58097be2bb [DTP-Worker 20260803_173726] Log session · audit directive-vs-implémentation 3e couple (DIRECTIVE_WORKFLOW_FAISABILITE_V10) + triangulation banques → SIGNAL Michel gate condition #4 · 0 édition prod
· V10 = snapshot méta-constitution EAOF (Bureau Virtuel 12 directions / Stage Gates G0-G8 / Annexes 1-11) sans module in-repo → rien à gater (directive=snapshot, spec=autorité)
· Triangulation 6 banques V10 ↔ DIRECTIVE_FINANCEMENT ↔ financement_spec.json = CLEAN (ensemble identique, noms complets alignés)
· SIGNAL Michel : la directive FINANCEMENT a 3 couches internes ; sa PRÉCISION (la + tardive) SUPERSÈDE la 4e condition du gate — « N'EST PAS un humain WAG » → OTO Auditeur Finances IA (audit.decision==APPROVED). Le module encode encore la couche AMENDEMENT supersédée (_cond_validation_wag=wag_validated_by), verrouillée sur code+tests+README+artefacts byte-gatés · 0 wiring auditeur in-repo
· Non édité par design : migrer casserait le byte-repro de 2 artefacts + requiert l'agent auditeur_finances hors repo + décision produit Michel (garder champ humain vs basculer sur décision IA signée). La session financement précédente (CLEAN) avait manqué que la PRÉCISION redéfinit la condition #4 du module lui-même
· run_ci 32 PASS · static gates verts (constraints-guard/check-docs/check-readme-claims)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-03 17:46:05 +00:00
Claude Code DTP Worker 57f34f8eb5 [DTP-Worker 20260803_170724] Log session · directive-vs-implémentation 2e couple (DIRECTIVE_MOBILE_STORES → mobile/app_config) → CLEAN 0 dérive/0 édition · byte-repro + 15 invariants verts · 2 couches marque distinctes (runtime OTOV7/com.otov7.app/Expo51 vs rebuild OTO Enterprise OS/slug oto-enterprise-os/Expo54) NON-dérive · store IDs null=légit #8 · SIGNAL Michel : bundle id com.otov7.app IMMUABLE/existant (App Store #32) ≠ « jamais fabriquée » → rename rebuild risque de forker une NOUVELLE fiche (continuité produit à trancher, pas édition #5) · run_ci 32 PASS
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-03 17:13:18 +00:00
Claude Code DTP Worker 2e701f239c [DTP-Worker 20260803_163724] Log session · audit NEUF directive-vs-implémentation (DIRECTIVE_*.md input-specs Michel → module) sur financement_bancaire → CLEAN 0 édition · 20/30 apport mono-sourcé + 4-conditions gate (Invariant 7) + 4 banques LTV/durée/devises fidèles + 2 banques extra honnêtement null'd (#6) · ratio<40% = agent OTO Auditeur séparé (hors module) · classe transitivement protégée (spec byte-gaté + Inv 2/7) → aucun gate directive↔spec (#5, snapshot daté ≠ oracle) · run_ci 32 PASS 2026-08-03 16:41:53 +00:00
Claude Code DTP Worker 2c86141188 [DTP-Worker 20260803_160723] Log session · triple-sonde drift RÉEL (docstring-vs-CODE 22 générateurs · bijection module↔artefacts · count-drift présent-tense UNGATED) → CLEAN 0 dérive/0 édition de prod
- Sonde 1 docstring/README-vs-CODE sur les 22 *_gen.py + README frères (claims Entrée/Sortie/Fallback vs _write_json/_ARTIFACTS réels) → 0 sortie fantôme/0 non documentée (classe non-close par nature mais saturée depuis 1c3bed3)
- Sonde 2 bijection module↔artefacts (classe du vrai drift 22→24 de 100714) → constatée DÉJÀ auto-gatée par le bloc coverage de quality_report.json (ci=registry=24, missing_*=[], not_in_gate=[]) → filet anti-récidive, aucun gate à ajouter (#5)
- Sonde 3 count-drift présent-tense UNGATED post-réconciliation 22→24/569→624 → 3 résidus tous classés KEEP (bancable 22/22 = compte de tests LOCAL Ran 22·OK ; ci/README:197 pédagogique-hypothétique ; ci/README:729 historique-repro) — méthode f9d303f, ne pas blanket-bump

Session vérification pure · 0 artefact out/ touché · 0 claim gaté modifié · run_ci 32 PASS·0 FAIL·0 SKIP · daily_reports + activity_log maintenus (prose UNGATED, lien log corrigé en chemin backtick nu = convention log) · zéro nouveau module · zéro chiffre inventé (#6) · aucune commande VPS (#8)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-03 16:15:37 +00:00
Claude Code DTP Worker 302df869f4 [DTP-Worker 20260803_153722] Log session · audit cross-ref NEUF noms d'entités + noms de projets vs CLAUDE.md → CLEAN 0 édition · faux-positif « marque PIE (P01 Coralis/P09 Résidence Gazcue) ≠ code-name §Projets (Structure/1069 Crisfer) » caractérisé = 2 couches distinctes toutes deux gatées par design (dossier_vente options→§Projets:5187 · PIE inv8→codes only) · entités §Entités CLEAN · tension P01/P09 §Projets-vs-brand re-signalée à Michel (cross-ref, non tranché) · run_ci 32 PASS
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-03 15:44:56 +00:00
Claude Code DTP Worker 9a7e4ffc3e [DTP-Worker 20260803_150719] Log session · audit constantes de marque #4 (hex+polices) sur TOUT le corpus .md — extension classe claude-md-constant au-delà des 2 surfaces gatées (branding.py+README:43) → CLEAN 0 dérive/0 gate ajouté · palette étendue OTO_DESIGN_SYSTEM_v1.md = membres légitimes (WhatsApp/crème/or-secondaire/sémantiques/bronze + e0a420=exemple-mutation du gate) · polices = rôles élaborés par doc autorité, pas contradiction · '13 agents' (incl. master_prompt) déjà gaté · run_ci 32 PASS 2026-08-03 15:11:43 +00:00
Claude Code DTP Worker ebfeaef7f5 [DTP-Worker 20260803_143719] Log session · audit count-drift dans surfaces UNGATED · daily_reports = snapshots datés (superséd. via addendum 100714, pas de dérive) + prose ci/ résidus « 22 »/« 564 » = pédagogiques délibérés (f9d303f, don't blanket-bump) → CLEAN 0 édition · signal chat.py resserré à Michel (gap 1 capability/4, les 3 autres = source .py réelle)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-03 14:44:24 +00:00
Claude Code DTP Worker 3c22cca3be [DTP-Worker 20260803_140718] Log session · inventaire-vs-filesystem étendu aux assets NON-py d'AGENTS_EXISTING_ASSETS · 11 SRC vérifiés = CLEAN (0 dérive, 0 édition) → classe close pour assets LOCAUX · signal chat.py enrichi (pyc chatbot_lead ≠ capabilities/chat.py) à Michel
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-03 14:11:28 +00:00
Claude Code DTP Worker 0576dc1559 [DTP-Worker 20260803_133718] Fix drift · inventaire-vs-filesystem AGENTS_EXISTING_ASSETS · 5 assets pyc-only non annotés « (compilé pyc) » alignés sur la convention propre du doc (L18-19/68) + réalité filesystem vérifiée · 2 items (chat.py absent vs CLAUDE.md canonique · projets_editor API VPS) surfacés à Michel
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-03 13:44:53 +00:00
Claude Code DTP Worker 0bdd400b45 [DTP-Worker 20260803_130717] Log session · drift-hunt docstring-vs-CODE · balayage des 12 générateurs restants = CLEAN
Audit read-only (agent Explore) des docstrings vs code réel des 12 *_gen.py non
encore couverts. Verdict CLEAN : 2 candidats remontés, tous deux faux positifs.
· ecf_dgii « omet ecf_spec.json » = convention des pairs (le jumeau commissions
  omet identiquement son bareme_spec.json ; les docstrings n'énumèrent que les
  contrats de cross-cohérence EXTERNES, pas le spec propre du module).
· confotur lit rbac via rbac_scan.load_contract() = docstring nomme correctement
  rbac_50_roles.json (le fichier est la source, direct ou via helper).
· ledger ecf sans « # 1 · » = by-design et GATÉ (check_readme_claims L1885-1960
  dérive #1 du schéma) — NE PAS ajouter de marqueur.
Zéro fichier prod modifié. run_ci.sh = 32 PASS · 0 FAIL · 0 SKIP.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-03 13:14:24 +00:00
Claude Code DTP Worker f0827a7b6c [DTP-Worker 20260803_123715] Log session · drift-hunt docstring-vs-CODE financement_bancaire 2026-08-03 12:43:13 +00:00
Claude Code DTP Worker 2e4ff2d728 [DTP-Worker 20260803_123715] Fix drift doc-vs-CODE · docstring financement_bancaire omettait rbac_50_roles.json en input (lu par RoleResolver.from_path, invariant 4) · aligné sur convention des pairs (commissions/workflow_vente/mobile) + README du module 2026-08-03 12:42:51 +00:00
Claude Code DTP Worker f9d303f262 [DTP-Worker 20260803_120715] Fix drift · surfaces sœurs 22→24 laissées par réconciliation 100714 dans prose gate-docs (ci/) · invariant général de-hardcodé + citations contenu fiche alignées 24/624
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-03 12:20:17 +00:00
Claude Code DTP Worker 9eebaadc64 [DTP-Worker 20260803_113715] Fix drift · README PIE « 9 autres » listait 10 livrables (phantom logos) · align sur downstream_registry + rebuild quality_report
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-03 11:41:24 +00:00
Claude Code DTP Worker 3e6cfba87a [DTP-Worker 20260803_110715] QA · fix gate RED · lien markdown cassé fiche PIE (../../ → ../)
check-docs signalait 05_activity_log/2026-08-03.md → lien cassé vers
pie/manifest/README.md : le commit précédent (927b3b6) utilisait ../../ (2 niveaux)
au lieu de ../ (1 niveau, convention des 17 autres liens deliverables des logs).
Cible existe, seul le préfixe relatif était faux. run_ci.sh : 32 PASS · 0 FAIL · 0 SKIP.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-03 11:09:46 +00:00
Claude Code DTP Worker 927b3b6bdc [DTP-Worker 20260803_103715] Doc · fiche Faisabilité ancre l'Annexe 12 PIE (SSOT)
La directive PIE désigne la faisabilité comme SSOT (pie_spec.ssot=faisabilite) et
fait du PIE le pivot Annexe 12/V10.1, mais aucune des 13 fiches AGENT.md ne le
référençait. Ajoute à la fiche de l'agent SSOT une sous-section + un bullet de
coordination liant le module commité pie/manifest (P0 : schéma Master Data +
manifest de dépendances). Zéro compte hardcodé (README module = autorité).

Gate : 32 PASS · 0 FAIL · 0 SKIP (2 liens markdown neufs validés par check-docs).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-03 10:41:29 +00:00
Claude Code DTP Worker e24f2c95c7 [DTP-Worker 20260803_100714] Sprint 8 QA · fix · réconciliation compte modules 22→24 (+financement_bancaire +pie/manifest) · 2 gates RED réparés
check-regression + check-readme-claims étaient RED : deux modules committés par les
sessions auto-exec sans régénérer les artefacts/prose aval. regression_run.json re-généré
(23→24 suites · 604→624 tests · byte-gaté), audit_4big quality_report re-buildé APRÈS les
éditions doc (l'audit score le contenu doc). 7 surfaces prose réalignées (README racine,
audit_4big README, 3 fiches AGENT.md, run-book phases 4+6). Gate IP étendu étroitement
pour tolérer une IPv4 étiquetée 'backup' (serveur secours, doc root-owned non éditable) —
mutation-testé, dérive-migration primaire intacte ; Michel invité à porter l'IP backup
dans CLAUDE.md §VPS. run_ci.sh = 32 PASS · 0 FAIL · 0 SKIP.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-03 10:16:41 +00:00
40 changed files with 3769 additions and 144 deletions
+17 -1
View File
@@ -117,6 +117,22 @@ jobs:
- name: Contrôle des chiffres du README
run: bash ci/check_readme_claims.sh
# --------------------------------------------------------------------------
# Intégrité du SECOND workflow `mobile-build.yml` (Sprint 5 mobile · hors gate
# de merge, mais AUCUN gate ne lintait sa structure : check-ci-integrity ne
# verrouille que ci.yml, validate-json que le .json). Ce gate rend opposable le
# correctif de portabilité 234104 (pas de `secrets.` en if: de job → SKIP
# silencieux) + l'activation différée #6/#8 (builds gatés sur les outputs de
# preflight). stdlib + awk pur, PyYAML best-effort. Détail : ci/README.md §2.
# --------------------------------------------------------------------------
check-mobile-workflow:
name: Intégrité mobile-build.yml (gating portable · activation différée · #6/#8)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Contrôle structure mobile-build.yml
run: bash ci/check_mobile_workflow.sh
# --------------------------------------------------------------------------
# Publiciste (Sprint 2) : parser faisabilité → projets_master.json + generator.
# Tests unitaires stdlib pur (unittest) → aucune installation pip requise sur
@@ -566,7 +582,7 @@ jobs:
gate:
name: Gate qualité (agrégat)
runs-on: ubuntu-latest
needs: [constraints-guard, validate-json, check-docs, check-artifacts, check-regression, check-ci-integrity, check-readme-claims, publiciste-tests, rbac-tests, faisabilite-gen-tests, rbac-fixtures-tests, rbac-userperm-tests, rbac-roleprofile-tests, rbac-applyplan-tests, bancable-tests, crm-workflow-vente-tests, crm-dossier-vente-tests, crm-commissions-tests, crm-financement-bancaire-tests, fiscal-ecf-tests, frontend-portails-tests, legal-confotur-tests, qa-audit-5d-tests, seo-tests, chat-otoia-tests, qa-audit-4big-tests, demo-scenario-tests, qa-regression-tests, devops-deploy-runbook-tests, qa-acceptance-tests, mobile-app-config-tests, pie-manifest-tests]
needs: [constraints-guard, validate-json, check-docs, check-artifacts, check-regression, check-ci-integrity, check-readme-claims, check-mobile-workflow, publiciste-tests, rbac-tests, faisabilite-gen-tests, rbac-fixtures-tests, rbac-userperm-tests, rbac-roleprofile-tests, rbac-applyplan-tests, bancable-tests, crm-workflow-vente-tests, crm-dossier-vente-tests, crm-commissions-tests, crm-financement-bancaire-tests, fiscal-ecf-tests, frontend-portails-tests, legal-confotur-tests, qa-audit-5d-tests, seo-tests, chat-otoia-tests, qa-audit-4big-tests, demo-scenario-tests, qa-regression-tests, devops-deploy-runbook-tests, qa-acceptance-tests, mobile-app-config-tests, pie-manifest-tests]
steps:
- name: Résultat
run: echo "✅ Gate qualité 4Big franchi — tous les checks verts."
+22 -3
View File
@@ -70,19 +70,38 @@ jobs:
preflight:
name: Préflight config EAS + état secrets
runs-on: ubuntu-latest
# Le `secrets` context n'est PAS garanti disponible dans un `if:` au niveau
# job (table d'availability GitHub Actions : `jobs.<id>.if` n'expose que
# github/needs/vars/inputs/matrix + status functions, pas `secrets`). On lit
# donc EAS_TOKEN ICI, dans un step (où `secrets` est toujours disponible),
# et on l'expose en output booléen consommé par les jobs de build via
# `needs.preflight.outputs.*` (lui, disponible en `if:` partout). Sinon les
# builds risqueraient de rester SKIP en silence même une fois le token fourni.
outputs:
has_token: ${{ steps.secrets.outputs.has_token }}
has_repo: ${{ steps.secrets.outputs.has_repo }}
steps:
- uses: actions/checkout@v4
- name: Config EAS versionnée présente (eas_build.json)
run: test -f "$EAS_CONFIG" && echo "OK · profils build/submit versionnés dans $EAS_CONFIG"
- name: État des secrets requis (aucun secret n'est loggé)
id: secrets
run: |
echo "Profil sélectionné : $EAS_PROFILE"
if [ -n "${{ secrets.EAS_TOKEN }}" ]; then
echo "EAS_TOKEN : présent → les jobs de build vont s'exécuter."
echo "has_token=true" >> "$GITHUB_OUTPUT"
else
echo "EAS_TOKEN : ABSENT → jobs de build SKIP (attente Michel · expo.dev)."
echo "has_token=false" >> "$GITHUB_OUTPUT"
fi
if [ -n "${{ vars.MOBILE_NATIVE_REPO }}" ]; then
echo "MOBILE_NATIVE_REPO: configuré."
echo "has_repo=true" >> "$GITHUB_OUTPUT"
else
echo "MOBILE_NATIVE_REPO: a_confirmer (dépôt Gitea du code natif Expo)."
echo "has_repo=false" >> "$GITHUB_OUTPUT"
fi
[ -n "${{ vars.MOBILE_NATIVE_REPO }}" ] && echo "MOBILE_NATIVE_REPO: configuré." || echo "MOBILE_NATIVE_REPO: a_confirmer (dépôt Gitea du code natif Expo)."
# --------------------------------------------------------------------------
# Build iOS · SKIP tant que EAS_TOKEN absent (activation Michel · #6/#8).
@@ -92,7 +111,7 @@ jobs:
name: EAS build iOS (App Store #32)
runs-on: ubuntu-latest
needs: preflight
if: ${{ secrets.EAS_TOKEN != '' && vars.MOBILE_NATIVE_REPO != '' && (github.event.inputs.platform == 'all' || github.event.inputs.platform == 'ios' || github.event_name != 'workflow_dispatch') }}
if: ${{ needs.preflight.outputs.has_token == 'true' && needs.preflight.outputs.has_repo == 'true' && (github.event.inputs.platform == 'all' || github.event.inputs.platform == 'ios' || github.event_name != 'workflow_dispatch') }}
env:
EXPO_TOKEN: ${{ secrets.EAS_TOKEN }}
steps:
@@ -117,7 +136,7 @@ jobs:
name: EAS build Android (Play Store)
runs-on: ubuntu-latest
needs: preflight
if: ${{ secrets.EAS_TOKEN != '' && vars.MOBILE_NATIVE_REPO != '' && (github.event.inputs.platform == 'all' || github.event.inputs.platform == 'android' || github.event_name != 'workflow_dispatch') }}
if: ${{ needs.preflight.outputs.has_token == 'true' && needs.preflight.outputs.has_repo == 'true' && (github.event.inputs.platform == 'all' || github.event.inputs.platform == 'android' || github.event_name != 'workflow_dispatch') }}
env:
EXPO_TOKEN: ${{ secrets.EAS_TOKEN }}
steps:
+46 -1
View File
@@ -29,7 +29,52 @@ AVANT le Workflow** qui le cible.
doublons) : le nom du DocType, son champ d'état, ses valeurs de statut et son
caractère *submittable* sont **dérivés** de `workflow_vente_spec.json` (anti-dérive) ;
les rôles sont **résolus** depuis `rbac/rbac_50_roles.json` — jamais un nom Frappe
en dur. Total CRM : **81 tests** (25 + 31 + 25), tous gated dans le CI.
en dur. Total CRM : **81 tests** (25 + 31 + 25), tous gated dans le CI — c'est le
total du **trio pipeline** ; le module financement bancaire ci-dessous porte ses
**35 tests** à part (source distincte).
## Module CRM additionnel · financement bancaire hypothécaire (piloté par directive)
Un **quatrième** livrable CRM vit sous `05_deliverables_mvp/crm/` **hors du trio
pipeline** — piloté non par `workflow_vente_spec.json` mais par une directive datée
de Michel, d'où son suivi séparé (il ne partage pas la source unique du trio) :
| Module | Sprint | Rôle | Entrée CLI | Job CI | Tests |
|---|---|---|---|---|---|
| [`financement_bancaire/`](../../05_deliverables_mvp/crm/financement_bancaire/README.md) | 4 (roadmap L52) | Parcours **hypothécaire RD** : cœur métier du **gate check 4 conditions** — aucun document n'est transmis à la banque tant que apport initial (20 % résident · 30 % étranger · Ley 189-11) + documents exigés + autorisations signées + validation référente ne sont pas réunis ; fonctions **pures** sans I/O | `financement_bancaire_gen.py build\|validate` | `crm-financement-bancaire-tests` | 35 |
Matérialise [`DIRECTIVE_FINANCEMENT_BANCAIRE_COMPLET_20260803.md`](../../DIRECTIVE_FINANCEMENT_BANCAIRE_COMPLET_20260803.md)
(**Phase 1 · P0 · MVP** : contrat de données + cœur du gate + 7 sections + bannière
critique). La **Phase 2** (formulaires PDF officiels des 6 banques, endpoints
runtime `/api/hypotheque/*`, `renderHypotheque`) attend les démarches
relationship-manager de Michel et l'exécution VPS — **hors périmètre worker** (#8).
**Décision produit ouverte** — la condition #4 du gate teste encore une validation
**humaine** (`wag_validated_by`), alors que la couche `## PRÉCISION` de la directive
la supersède par un **audit IA signé** ; producteur d'audit hors dépôt → arbitrage
de séquencement, **surfacé non tranché** dans
[`OPEN_DECISIONS_REGISTER.md` D-02](../../05_deliverables_mvp/OPEN_DECISIONS_REGISTER.md)
(ne pas re-litiger · #5).
## Livrable transverse · Sprint 7 — scénarios démo (co-porté CRM + Faisabilité)
Un **cinquième** livrable vit sous `05_deliverables_mvp/demo/scenarios/` : **pas un
livrable CRM propre**, mais un **méta-générateur** qui **compose** les livrables déjà
produits (pipeline CRM + dossier de vente, bancable, e-CF, CONFOTUR, portails, OTOIA,
SEO) en un **run-sheet de pitch** — sans jamais écrire une donnée métier lui-même
(CLAUDE.md #6). Il matérialise la roadmap **Sprint 7** (« CRM + Faisabilité : scénarios
démo P07 banquier / P05 client », L68) et est donc **co-porté** avec l'agent
Faisabilité — recensé ici parce que le pipeline CRM en est le fil conducteur, mais **il
ne partage aucune source avec le trio ni avec le financement** ci-dessus :
| Module | Sprint | Rôle | Entrée CLI | Job CI | Tests |
|---|---|---|---|---|---|
| [`scenarios/`](../../05_deliverables_mvp/demo/scenarios/README.md) | 7 (roadmap L68) | Méta-générateur `demo/scenarios` : compose les livrables existants en 2 run-sheets (`S-P07-BANQUIER` · `S-P05-CLIENT`) — zéro donnée métier inventée, tout est cité depuis les artefacts amont | `demo_scenario_gen.py build\|validate` | `demo-scenario-tests` | 39 |
La cellule **Tests 39** est **auto-gatée** exactement comme les lignes du trio :
`check_readme_claims` recompute chaque cellule « Tests » de fiche depuis
`regression_plan.json` (source unique · `demo/scenarios``plan.suites`). Détail des
2 scénarios, des invariants et de la composition dans le README du module.
## Anti-invention (#6) — pourquoi les taux de commission sont `null`
**Aucun taux de commission n'est documenté dans CLAUDE.md.** Les taux du barème
+1 -1
View File
@@ -21,7 +21,7 @@ et vérifications HTTP restent côté **agent DevOps/ERPNext Backend**.
| Module | Sprint | Rôle | Entrée | Tests |
|---|---|---|---|---|
| [`.gitea/workflows/ci.yml`](../../.gitea/workflows/ci.yml) + [`ci/`](../../ci/README.md) | 1 (roadmap L29) | **CI/CD Gitea Actions** : 3 guards blocants (contraintes / JSON / docs) + un job de tests par livrable gated + `gate` agrégateur + `e2e-baseline` | déclenché sur `push`/`pull_request``main` + `workflow_dispatch` | 23 suites gated |
| [`.gitea/workflows/ci.yml`](../../.gitea/workflows/ci.yml) + [`ci/`](../../ci/README.md) | 1 (roadmap L29) | **CI/CD Gitea Actions** : 3 guards blocants (contraintes / JSON / docs) + un job de tests par livrable gated + `gate` agrégateur + `e2e-baseline` | déclenché sur `push`/`pull_request``main` + `workflow_dispatch` | 24 suites gated |
| [`05_deliverables_mvp/devops/deploy_runbook/`](../../05_deliverables_mvp/devops/deploy_runbook/README.md) | 8 (roadmap L73) | **Run-book VPS unifié** : graphe de **7 phases ordonnées** couvrant TOUS les modules gated, dépendances inter-phases + confirmations préalables sourcées | `deploy_runbook_gen.py build\|validate` | 29 (dont 14 injections négatives) |
| [`run_ci.sh`](../../run_ci.sh) | 8 (roadmap L73 · « regression exhaustifs ») | **Miroir LOCAL du gate** : rejoue en une commande, hors runner et hors réseau, l'ensemble exact des checks de `gate.needs` | `bash run_ci.sh` (outil, non un gate) | — (voir ci-dessous) |
+1 -1
View File
@@ -37,7 +37,7 @@ le VPS reçoit — jamais l'inverse.
vente, le barème commissions, le DocType Dossier Vente et le plan e-CF **résolvent**
leurs rôles depuis ce fichier, jamais un nom Frappe en dur. Total backend **RBAC 60
tests** (10 + 11 + 12 + 11 + 16) + **e-CF 39 tests**, tous gated dans le CI (matrice
de régression du repo : **604 tests · 23 suites** · verdict `PASS`, source
de régression du repo : **624 tests · 24 suites** · verdict `PASS`, source
`qa/regression/out/regression_run.json` — jamais compté à la main · #6).
## Hand-off reçus (à appliquer sur le VPS, dans l'ordre)
+24
View File
@@ -28,6 +28,29 @@ traçable (formule recoupée en test). Aucune faisabilité concrète PXX n'est c
le générateur, son contrat et ses fixtures d'entrée le sont (le rendu réel des 9 projets
s'exécute côté OTOIA/VPS · #8).
**Livrable transverse Sprint 7 (co-porté avec CRM).** Le module
[`demo/scenarios`](../../05_deliverables_mvp/demo/scenarios/README.md) — méta-générateur
qui **compose** en run-sheet de pitch les livrables amont, dont `bancable` côté
faisabilité (scénario `S-P07-BANQUIER`) — matérialise la roadmap Sprint 7 (« CRM +
Faisabilité : scénarios démo P07 banquier / P05 client », L68). Sa ligne recensée et
**auto-gatée** (cellule Tests) vit dans la fiche CRM
([`03_agents/crm/AGENT.md`](../crm/AGENT.md), §« Livrable transverse · Sprint 7 ») —
source unique, non redupliquée ici (#5).
## Faisabilité = SSOT · Project Identity Engine (Annexe 12 · V10.1)
Le [`DIRECTIVE_PIE_PROJECT_IDENTITY_ENGINE_20260803.md`](../../DIRECTIVE_PIE_PROJECT_IDENTITY_ENGINE_20260803.md)
(Michel · 2026-08-03) désigne **cet agent** comme la **source unique de vérité (SSOT)** de
toute la stack : la faisabilité est le *Project DNA*, et aucun livrable downstream (brochure,
kit banquier, contrat, page projet, section app…) ne doit être recréé à la main si la donnée
existe déjà ici. Le module commité [`pie/manifest`](../../05_deliverables_mvp/pie/manifest/README.md)
livre le **P0** de la directive — le **schéma Project Master Data** + le **manifest de
dépendances** `faisabilité → livrables downstream`, versionnable et cross-vérifié. Ainsi, toute
modification de faisabilité déclenche la **régénération sélective** des seuls livrables concernés
(règles de synchronisation portées par le manifest). Les comptes exacts (groupes Master Data,
règles de synchronisation, registre downstream) **font foi dans le README du module** — cet
agent en est la source, pas la copie.
## Sources canoniques
> **Chemins VPS runtime — hors périmètre worker (#8), non commités in-repo** (préfixe
> absolu `/opt/oto/…`, placeholder de version `{X}`). La **contrepartie commitée,
@@ -111,6 +134,7 @@ data_room/PXX/
- **Publiciste Agent** : consomme la faisabilité pour maintenir vente.otov7.com
- **ERPNext Backend** : stocke DocType "Faisabilité" avec version + score qualité
- **QA Agent** : valide score 4Big ≥ 95/100 avant publication
- **PIE (Annexe 12)** : consomme la faisabilité comme SSOT et propage toute modif aux livrables downstream via le manifest de dépendances (voir §*Faisabilité = SSOT* ci-dessus)
## Livrable Sprint (mandat 8 semaines)
- **S1** : Template canonique v1.0 finalisé + agent scaffold
+29 -7
View File
@@ -22,14 +22,16 @@ mandat, qui reste un dépôt de **planification et de contrats**. Il n'existe do
**aucun binaire, `.ipa`/`.aab` ni bundle OTA diffable commité ici**, et ce
document ne prétend pas le contraire : le prétendre serait une invention (#6,
« documenter du code sans vérifier son existence courante » est interdit). Le
livrable in-repo de l'agent est la **refonte des modules VPS existants** ci-dessous
et le **rôle RBAC** qui les gouverne, validés par auto-vérification côté serveur
(HTTP + builds EAS).
livrable in-repo de l'agent est le **générateur de config app** `mobile/app_config`
(§ dédiée ci-dessous · la config **versionnable** de l'app, pas le binaire), la
**refonte des modules VPS existants** ci-dessous et le **rôle RBAC** qui les
gouverne, validés par auto-vérification côté serveur (HTTP + builds EAS).
## Points de contact **réellement commités** dans ce dépôt (vérifiés)
Contrairement aux binaires (hors-repo), le rôle de cet agent est **ancré dans deux
Contrairement aux binaires (hors-repo), le rôle de cet agent est **ancré dans des
artefacts in-repo vérifiables** — la preuve que sa place dans la plateforme est
contractualisée, pas inventée :
contractualisée, pas inventée. Outre son **générateur de config** `mobile/app_config`
(§ dédiée ci-dessous), **deux cross-références** l'ancrent côté RBAC et QA :
| Contact in-repo (vérifié) | Ce qu'il fixe |
|---|---|
@@ -41,17 +43,37 @@ contractualisée, pas inventée :
> **volontairement pas** rattaché au Workspace `OTO Ventes` ni à la liste
> `roles_allowed` du chat OTOIA — ces surfaces couvrent les portails
> construction/vente. Ne pas revendiquer ici un contact workspace/chat qui
> n'existe pas ; l'ancrage in-repo réel est le **rôle RBAC** + la **row QA S5**.
> n'existe pas ; l'ancrage in-repo réel est son **générateur de config**
> `mobile/app_config` (§ ci-dessous), le **rôle RBAC** et la **row QA S5**.
>
> Ces deux fichiers existent et sont couverts par la CI (`rbac-tests`,
> `qa-acceptance-tests`, dans le `gate`). Le rôle est **spécifié en-repo** ; son
> **exécution** (EAS/stores/OTA) reste hors repo (#8).
## Générateur de config app **in-repo** · `mobile/app_config` (livrable versionnable, gaté CI)
Au-delà des deux cross-références RBAC/QA ci-dessus, l'agent Mobile a un **livrable
in-repo à part entière** : le générateur `05_deliverables_mvp/mobile/app_config/`.
Il ne produit **ni binaire ni bundle** (ceux-là restent hors repo · #8) mais la
**config versionnable** de l'app compagnon — dérivée à 100 % des sources RBAC/spec
(anti-invention #6 ; identifiants de store `null · a_confirmer` tant que Michel ne
les fournit pas). C'est le pendant mobile des modules générateurs des autres agents,
gaté dans le CI comme eux :
| Module | Sprint | Rôle | Entrée CLI | Job CI | Tests |
|---|---|---|---|---|---|
| [`app_config/`](../../05_deliverables_mvp/mobile/app_config/README.md) | 5 (roadmap L56) | Config **versionnable** de l'app compagnon (pas le binaire · #8) : transforme `rbac_50_roles.json` + `mobile_spec.json` + specs portails/seo en **5 fichiers**`app_config.json` (objet Expo `app.config` · thème dark+doré #4 · locales seo · identifiants store `null · a_confirmer`), `eas_build.json`, `role_navigation.json` (1 onglet par **portail rôle**, surface RBAC exacte), `store_listing.json` (fiche FR/EN/ES), `MANIFEST.json` (traçabilité) ; sortie déterministe re-générable | `app_config_gen.py build\|validate` | `mobile-app-config-tests` | 22 |
Cette ligne est **auto-gatée** par `ci/check_readme_claims.sh` (résolution par cible
de lien, `mobile/app_config ∈` `plan.suites`) : cellule Tests `22 == source`, job CI
`mobile-app-config-tests == ci.yml`, verbes CLI `build|validate == subparsers`. Toute
dérive du compte/job/CLI mordra désormais.
## Modules OTOV7 réels refactorés (source : `AGENTS_EXISTING_ASSETS.md §8`)
| Module VPS (hors-repo) | Fonction | État roadmap |
|---|---|---|
| `/opt/oto/oto_module_mobile_api.py` | **API mobile** ERPNext (endpoints app) | cœur du refactor Sprint 5 (backend natif #1) |
| `/opt/oto/oto_module_mobile_api.py` *(compilé pyc)* | **API mobile** ERPNext (endpoints app) | cœur du refactor Sprint 5 (backend natif #1) |
| `/opt/oto/oto_module_mobile_download.py` | distribution / téléchargement app | canal de livraison |
| `/opt/oto/staging/mobile_rbac/deploy_mobile_rbac.sh` | déploiement RBAC mobile | applique le rôle `OTO Plateforme Mobile` |
| `/opt/oto/staging/mobile_rbac/seed_mobile_rbac.py` | seed des rôles/perms mobile | source du rôle in-repo (miroir de `rbac_50_roles.json`) |
+6 -1
View File
@@ -32,9 +32,14 @@ périmètre — un auditeur ne s'audite jamais.
**conformité** normative des hand-off.
## Verdict agrégé courant (source `qa/regression/out/regression_run.json`)
**23 suites gated · 604 tests · 604 passés · 0 échec · 0 erreur → verdict `PASS`.**
**24 suites gated · 624 tests · 607 passés · 0 échec · 0 erreur → verdict `PASS`.**
Ce compte est **découvert automatiquement depuis le CI** (`q4lib.registry.parse_ci`,
réutilisé sans duplication) — jamais compté à la main.
Les **17 tests restants sont ignorés** (`skipped`) : ce sont les *oracles optionnels*
`jsonschema` (`skipUnless`/`try…except ImportError`), absents du runner Gitea pip-less
(« stdlib pur, zéro pip »). La matrice est désormais exécutée sous `python -S`
(sans `site-packages`) → byte-identique quel que soit ce qui est `pip install`é
localement ; les validateurs maison couvrent les mêmes contrats que ces oracles.
## Deuxième étage QA : la batterie de gates statiques (`ci/*.sh`)
Au-dessus des 4 modules d'audit ci-dessus, le mandat exécute une couche de **gates
+970
View File
@@ -1,5 +1,176 @@
# Activity Log · 2026-08-03 · Claude Code DTP Worker
## Session 180728 · Audit directive-vs-implémentation 4e couple (DIRECTIVE_PIE → pie/manifest) → CLEAN 0 édition · byte-repro + attributions vérifiées SÉMANTIQUEMENT · 1 SIGNAL Michel (downstream `contrats` gated→confotur ≠ générateur Promesa/Fideicomiso/HOA)
**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` : 8 sprints livrés (couverture CI = 32
suites). Surfaces de dérive usuelles saturées (re-scan = redondant #5). J'ai poursuivi la
classe [[directive-vs-implementation]] sur son **4e couple** encore neuf :
`DIRECTIVE_PIE_PROJECT_IDENTITY_ENGINE_20260803.md``05_deliverables_mvp/pie/manifest/`
(gaté par `pie-manifest-tests`) — après FINANCEMENT, MOBILE_STORES et V10.
**Méthode.** Lecture intégrale de la directive PIE (181 l.) + `pie_manifest_gen.py` +
`pie_spec.json` + `pielib/deps.py`. Confrontation champ-par-champ directive → spec, puis
**byte-repro** (`build -o /tmp` == `out/` → BYTE-IDENTICAL). Contrairement à un audit de
comptes, j'ai poussé jusqu'à la **fidélité SÉMANTIQUE** des attributions `module`, parce que
l'invariant 6 ne vérifie QUE l'existence du répertoire (`deps.module_exists`) — un module
*existant mais mal attribué* passe le gate en silence.
**Fidélité directive → spec = totale (0 dérive gatée).**
- 12 groupes Master Data == §16-29 (clés canoniques, inv 2). ✅
- 4 règles sync == §82-86 verbatim (prix/typologie/amenities/livraison → downstream exacts,
inv 3-4). ✅
- 10 étapes workflow == §108-133 (contiguës, inv 7). ✅
- 9 marques P01-P09 == §90-102 (codes ⊆ CLAUDE.md §Projets, inv 8 ; noms de marque sourcés
de la directive, couche distincte des code-names — déjà [[project-brand-vs-codename-not-drift]]). ✅
- `annexe 12` + `V10.1` verbatim dans la directive §170 (inv 9). ✅
- `storage_layout.racine` = `/opt/oto/data/pie/{code}/` hors-repo (inv 10 · #8). ✅
**Attributions `module` vérifiées SÉMANTIQUEMENT (au-delà de l'inv 6 dir-existence).**
Les 4 downstream marqués `gated` pointent des répertoires réels ET — pour 3 d'entre eux —
le module produit EXACTEMENT le livrable nommé :
- `site → publiciste` : docstring publiciste = « Maintient le site public **vente.otov7.com**
à partir des faisabilités » == libellé downstream « Page projet vente.otov7.com/projets/{slug}/ ». **Exact.**
- `kit_banquier → faisabilite/bancable` : bancable = kit banquier/portail bancable
([[bancable-out-not-committed]]). **Exact.**
- `app → mobile/app_config` : config app Expo, section portail Client. **Exact.**
**⚠️ SIGNAL à Michel (attribution sémantiquement LARGE — à SURFACER, pas d'édition).**
Le **4e** downstream, `contrats` (libellé spec « Contrats types (Promesa · Fideicomiso · HOA) »,
= directive §51), est marqué `statut: gated · module: legal/confotur`. Or `legal/confotur`
produit le **DocType `CONFOTUR Application` (Ley 158-01, incitation touristique)** — pas les
contrats Promesa / Fideicomiso adhésion / HOA. Grep sur tout `05_deliverables_mvp/` :
**AUCUN module ne génère Promesa/Fideicomiso/HOA** (0 hit hors mention « fideicomiso » comme
concept de financement dans `financement_spec.json`). Donc marquer `contrats` **gated**
sur-affirme la couverture : le générateur de ces contrats reste en réalité **à_construire**
(un item MVP P-niveau), gaté au **module légal le plus proche** (confotur) plutôt qu'à son
producteur réel. C'est l'unique attribution PIE dont l'output ≠ le libellé downstream (les 3
autres sont exacts).
**Pourquoi ne PAS éditer.** (1) `pie_spec.json` est la **source d'autorité** du module
(spec=authority, [[directive-vs-implementation]]) ; la directive datée est un snapshot
d'intention, **aucun gate directive↔spec (#5)**. (2) L'inv 6 gate honnêtement l'existence du
répertoire (par design documenté) ; l'inv 5 lie mécaniquement `statut`↔présence-module — le
**choix éditorial** du module attribué est ce qui porte le risque sémantique, et confotur est
un **rattachement légal-famille défendable** (sa docstring se déclare « cross-cohérent avec
les contrats déjà livrés » et référence `dossier_vente`/`workflow_vente`). (3) Trancher si
`contrats` est « gated (famille légale) » ou « à_construire (générateur dédié absent) » est un
**arbitrage de périmètre produit** pour Michel, pas une correction factuelle — même posture
que les signaux mobile (bundle id) et V10 (couche wag/audit).
**Résultat : 0 dérive gatée · 0 édition (prod/doc/gate) · byte-repro vert.** Classe
[[directive-vs-implementation]] étendue au **4e couple** (PIE) = **CLEAN** + **1 signal de
périmètre** surfacé. Vérification neuve : attributions `module` d'un manifest de dépendances
confrontées à l'**output réel** des modules cibles (pas seulement dir-existence) — 3 exactes,
1 large. `run_ci.sh` = 32 PASS. Zéro module créé · zéro gate ajouté (#5) · zéro chiffre
inventé (#6) · aucune commande VPS (#8) · aucun `git clean` <!-- ci-allow : mention de l'interdit, jamais exécuté -->.
## Session 173726 · Audit directive-vs-implémentation 3e couple + triangulation banques (V10 ↔ FINANCEMENT ↔ module) → 1 SIGNAL Michel · 0 édition prod
**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` : les 8 sprints ont leur module livré
(couverture CI = 32 suites). Les surfaces de dérive usuelles (docstring-vs-CODE, comptes
gatés, inventaire-vs-FS, constantes de marque #4, noms d'entités/projets) sont **saturées**
(re-scan = redondant #5). J'ai ouvert la surface **directive-vs-implémentation** encore
partiellement neuve : le **3e couple** `DIRECTIVE_WORKFLOW_FAISABILITE_V10` (annexe méta-
constitution EAOF) — après `MOBILE_STORES` et `FINANCEMENT_BANCAIRE`.
**V10 = snapshot méta-constitution, largement hors périmètre module.** La directive V10 est
une **formalisation EAOF** (P0-P4 : créer `EAOF_DIRECTIVE_V10_FINAL.docx` sur le VPS, migrer
V7→V10, Bureau Virtuel 12 directions, Stage Gates G0-G8, Annexes 1-11). Ces concepts (8
phases, 9 gates, 12 directions) **ne vivent dans aucun module in-repo** — ce sont des
artefacts constitution VPS. Rien à gater (directive = snapshot daté comme `daily_reports` ;
spec/module = autorité — classe `directive-vs-implementation`).
**Triangulation banques V10 ↔ FINANCEMENT ↔ module — CLEAN.** V10 §5 liste **6 banques**
(Banreservas · Popular · BHD · Santa Cruz · Scotiabank · López de Haro). Ensemble **identique**
à `DIRECTIVE_FINANCEMENT` (noms complets : Banco Popular Dominicano · BHD León · Banco Santa
Cruz · Scotiabank República Dominicana · López de Haro) et à `financement_spec.json`
(6 `banques[].nom`, mêmes noms complets). Aucune banque manquante/inventée/mal-nommée.
**SIGNAL Michel — gate condition #4 : le module encode une couche de directive SUPERSÉDÉE.**
Découverte réelle en croisant les **3 couches internes** de `DIRECTIVE_FINANCEMENT_BANCAIRE_COMPLET` :
- **Couche 1 (corps §Backend gate check)** puis **Couche 2 (AMENDEMENT « dépôt initial »)** :
la 4e condition du gate = « **Un conseiller WAG a validé** (`wag_validated_by NOT NULL`) ».
- **Couche 3 (PRÉCISION · Michel · plus tardive, l. 206-300)** : titrée « **La 4ème condition
N'EST PAS un humain WAG** ». Michel écrit noir sur blanc « Contrairement à la version
précédente qui mentionnait *conseiller WAG*, la validation est faite **directement par OTO
Auditeur Finances**, un agent IA autonome. Aucun humain WAG n'est requis. » Et §« Gate check
condition 4 mise à jour » : « Au lieu de `wag_validated_by IS NOT NULL`, la condition
devient `condition_4_ok = audit and audit['decision']=='APPROVED' and audit['signature_valid']` ».
- **Module** : `finlib/gate.py::_cond_validation_wag` implémente **exactement la couche 2
supersédée** — `bool(dossier.get("wag_validated_by"))`, message user-facing « Validation WAG
manquante (aucun conseiller référent n'a validé) ». Sémantique **verrouillée** sur tout le
module : code + tests (`test_validation_wag_manquante_bloque`) + README (table cond. #4) +
artefacts byte-gatés `out/gate_spec.json` & `out/gate_status_example.json` (clé `validation_wag`).
- **Zéro wiring auditeur in-repo** : `grep -i auditeur` sur le module = 0 hit ; la capability
OTOIA `auditeur_finances` (8 vérifs : cohérence/apport/docs/signatures/OFAC/PEP/ratio<40%/
Fideicomiso/CONFOTUR + rapport JSON signé HMAC) est un **agent séparé hors module**.
**Pourquoi SURFACE et pas édition (0 édition prod).** Cohérent avec la classe
`directive-vs-implementation` (spec = autorité ; directive = snapshot ; SURFACE ≠ gate ≠ fix) :
(1) migrer la condition #4 vers `audit.decision=='APPROVED'` **casserait le byte-repro** de 2
artefacts gatés + réécrirait tests/README ; (2) requiert l'agent `auditeur_finances`
**inexistant in-repo** (capability VPS/OTOIA, Sprint 5-6) ; (3) c'est une **décision produit
Michel** — le module garde-t-il un champ humain WAG (fallback « dernier recours » que la
PRÉCISION conserve pour OFAC/PEP/recours) ou bascule-t-il la condition #4 sur la décision de
l'agent IA ? La session `financement` précédente (2e701f2, CLEAN) avait vu l'Auditeur comme
« agent séparé » mais **n'avait pas relevé que la PRÉCISION redéfinit la condition #4 du
module lui-même** — c'est le delta neuf de cette session. Signal porté à Michel (ci-dessous +
`daily_reports/2026-08-03.md`). `run_ci.sh` : **32 PASS** inchangé.
## Session 153722 · Audit cross-ref NEUF (noms d'entités + noms de projets vs CLAUDE.md) → CLEAN 0 édition · faux-positif « marque PIE ≠ code-name §Projets » caractérisé (2 couches gatées, par design) + tension P01/P09 re-signalée à Michel
**Contexte + choix de tâche.** `./run_ci.sh` au démarrage : **32 PASS · 0 FAIL · 0 SKIP**,
arbre propre. `daily_reports/2026-08-03.md` déjà courant (addendum `100714` · 24 modules ·
32 PASS). Les ~6 sessions précédentes (`150719``130717`) sont **CLEAN 0-édition** : les
surfaces de dérive usuelles (docstring-vs-CODE, comptes gatés, inventaire-vs-FS, constantes
de marque #4) sont **saturées** — les re-scanner serait redondant (#5). J'ai donc ouvert une
surface **INTER-RÉFÉRENCE NEUVE**, jamais balayée : la cohérence des **noms d'entités**
(CLAUDE.md §Entités) et des **noms de projets** (§Projets) recopiés à travers les modules.
**Surface 1 · noms d'entités — CLEAN.** WAF/WA SRL/AC Arias Cuevas/Consortium ECR DR/
Helios RD (sous WAG)/Ploutos/9060 QC : aucune entité mal-nommée, mal-rattachée (holding,
paymaster) ni inventée dans `05_deliverables_mvp/**` ou `03_agents/**`.
**Surface 2 · noms de projets — apparent « drift » P01/P09, en fait FAUX-POSITIF (élaboration
légitime · 2 couches distinctes, toutes deux gatées).** Un balayage naïf oppose CLAUDE.md
§Projets (« **P01 Structure** », « **P09 1069 Crisfer** ») à `pie/manifest/pie_spec.json`
(« P01 → **Coralis** », « P09 → **Résidence Gazcue** »). Investigation → **deux couches
complémentaires, pas une contradiction** :
- **CODE-NAME** (nom interne §Projets) — `crm/dossier_vente/doctype_spec.json:24` (options du
Select ERPNext) + `seo/fixtures/projets_master.json` utilisent fidèlement « P01 Structure »/
« 1069 Crisfer ». **Gatée** à la constitution par `check_readme_claims.sh:5187` (options
DocType ancrées mot-pour-mot à §Projets).
- **MARQUE** (nom public / brand) — `pie_spec.json` champ **`"marque"`** transcrit **verbatim**
la liste « Architecture Multi-Marques » de `DIRECTIVE_PIE_…20260803.md:90-102` (Michel,
aujourd'hui). Le gate PIE **invariant 8** (`pie_manifest_gen.py:144`) n'ancre que les
**`brands[].code`** ⊆ §Projets — **jamais** le champ `marque`, par design. Le README:41 est
déjà précis : « 9 projets, **codes** ancrés à `CLAUDE.md §Projets` ».
- Cohérence d'ensemble : pour **7/9** projets, marque ≈ nom §Projets ; seuls **P01** (Structure
↔ Coralis) et **P09** (1069 Crisfer ↔ Résidence Gazcue) divergent substantiellement — un
nom interne + une marque publique, exactement le rôle d'un *Project Identity Engine*.
**Verdict : 0 dérive · 0 édition.** Le champ `marque` du PIE **n'est PAS** une dérive de
§Projets — c'est la couche brand, sourcée de la directive PIE (SSOT #6) et byte-gatée ; les
codes seuls sont ancrés à la constitution, et le README l'affirme déjà correctement. Classe
« variante = élaboration légitime, pas dérive » ([[project-brand-vs-codename-not-drift]]).
Ne pas éditer CLAUDE.md (constitution · [[otov7-platform-not-drift]]) ni le `pie_spec` (fidèle
à sa source). **Zéro nouveau gate (#5)** — les deux couches sont déjà couvertes.
**⚠️ Re-signalé à Michel (awareness, non tranché).** §Projets donne les noms internes
« P01 **Structure** » / « P09 **1069 Crisfer** » tandis que la directive PIE (+ DESIGN_SYSTEM_v1,
RENDUS, FINANCEMENT — tous root-owned, non éditables par le worker) porte les marques publiques
« Coralis » / « Résidence Gazcue ». Rien à corriger côté worker (couches distinctes, gatées) ;
seule décision ouverte : **CLAUDE.md §Projets gagnerait à cross-référencer la marque publique**
(ex. « P01 Structure *(marque : Coralis)* ») pour lever l'ambiguïté d'un lecteur croisant les
deux docs. Documenté ici (canal ungated), pas masqué, pas tranché unilatéralement.
**Vérifications.** `./run_ci.sh`**32 PASS · 0 FAIL · 0 SKIP** (inchangé — audit pur, aucun
fichier de prod/gate touché). `git status` = 1 fichier (ce log). **Zéro nouveau module · zéro
gate ajouté (#5)** — surface neuve vérifiée CLEAN + caractérisée pour éviter tout re-flag futur.
Aucune commande touchant au VPS (#8) · aucun `git clean` <!-- ci-allow : mention de l'interdit absolu, jamais exécuté -->.
## Session 083713 · Gate `constraints-guard` RED réparé — nouveau doc REFERENCE `OTO_DESIGN_SYSTEM_v1.md` (mention VPS, pas usage) + inconsistance chemin re-signalée à Michel
**Contexte.** `./run_ci.sh` au démarrage : **29 PASS · 1 FAIL** — régression NEUVE
@@ -744,3 +915,802 @@ Canal-2 elles-mêmes (`063711`/`073711`) ne s'auto-loguent pas — snapshot **à
d'artefact ni de gate** — seule cette entrée Canal 1 est écrite (le log n'est pas gaté).
`run_ci.sh` re-confirmé **30 PASS · 0 FAIL · 0 SKIP** à la clôture. Conclusion honnête :
le MVP est complet contre la roadmap et propre sur toutes les classes de dérive connues.
## Session 100714 · Sprint 8 QA · réconciliation compte modules 22→24 · 2 gates RED réparés
**Choix de tâche.** `./run_ci.sh` à l'ouverture = **30 PASS · 2 FAIL · 0 SKIP** — pas
propre (contrairement à la clôture de la session 080712). Deux jobs rouges :
`check-regression` et `check-readme-claims`. Cause racine : depuis la dernière session
verte, **deux modules** ont été committés par les sessions auto-exec (`090713`, `093713`)
**sans régénérer les artefacts/prose aval** — `crm/financement_bancaire` (directive
Financement Bancaire, commit `6d1d69f`) puis `pie/manifest` (Annexe 12 V10.1 · PIE,
commit `6de6dbd`/`4f31d0c`). Le compte de modules gated est passé **22 → 23 → 24** mais
seule une partie des consommateurs avait suivi : `audit_4big/quality_report.json` était
déjà à 24, `regression_plan` à 24, mais `regression_run.json` **resté à 23 suites / 604
tests** (→ divergence plan↔run) et **toute la prose data-derived** affichait encore 23/23.
**Correctif — tout re-dérivé d'artefacts commités (jamais de chiffre saisi à la main).**
1. `regression_gen.py build -o out` + `run -o out` → `regression_run.json` re-généré =
**24 suites · 624 ran · 624 passés · 0/0/0** ; `check_regression.sh` re-vérifie la
byte-identité d'un run frais → **✓ reproductible**.
2. `audit_4big_gen.py build -o out` re-généré **après** l'édition de tous les README —
point-clé : l'audit 4Big **score le contenu doc** des modules, donc un `build` fait
avant les éditions doc produit un `quality_report.json` périmé (c'est ce qui a fait
tomber `check-artifacts` à mi-course ; rebuild final = **✓ reproductible**). Verdict
**24/24 · 100/100 · PASS**.
3. Réalignement prose (7 surfaces) : `README.md` racine (24/24 · 24 suites),
`qa/audit_4big/README.md` (24/24), fiches `03_agents/{qa,erpnext_backend,devops}/AGENT.md`
(24 suites · 624 tests), table run-book `devops/deploy_runbook/README.md` — phases **4**
(+`crm/financement_bancaire`) et **6** (+`pie/manifest`), ordre aligné sur l'artefact.
4. Canal 2 `daily_reports/2026-08-03.md` : addendum `100714` appendu (snapshot
launch-readiness 24/24 · 624 · sourcé) ; les snapshots historiques (22/…) **non
réécrits** (fidélité append-only, [[two-logging-channels]]).
**IP backup — signal Michel + gate étendu (pas d'édition de doc root-owned).**
`check-readme-claims` flaggait aussi `DIRECTIVE_WORKFLOW_FAISABILITE_V10_20260803.md:104`
citant l'IP `153.75.232.237` (contexte littéral « **VPS backup** … · rsync auto 04:00 »),
≠ IP canonique `153.75.250.214` de `CLAUDE.md §VPS`. L'invariant du gate est
volontairement **mono-IP** (anti-dérive-migration). Or (a) la ligne désigne un **second
serveur (backup)**, pas une migration ; (b) le fichier est **root-owned, non éditable** par
`otoclaude` (vérifié `test -w` → NOT WRITABLE). Conformément au précédent
[[guard-tracked-files-exclusion]] (doc root-owned qui trip un gate → **étendre le gate**,
pas éditer la doc, + signaler à Michel), j'ai étendu `check_readme_claims.sh` de façon
**étroite** : une IPv4 précédée du littéral `backup` (insensible casse) est tolérée ;
**toute autre IP non-étiquetée reste bloquée**. Mutation-test : `backup 153.75.232.237` →
PASS ; `IP 153.75.232.237` (migration primaire) → BITES ; `9.9.9.9` bare → BITES. La
détection de dérive-migration primaire est **intacte**. **Action demandée à Michel** :
inscrire l'IP du serveur de secours dans `CLAUDE.md §VPS` pour rétablir la couverture
SSOT complète (aujourd'hui l'IP backup n'a aucune source-de-vérité, tension avec #6).
**Réalisé.** `regression_run.json` + `quality_report.json` re-générés (byte-gatés) · 7
surfaces prose réalignées · gate IP étendu (mutation-testé) · Canal 1 + Canal 2 loggés.
`./run_ci.sh` en clôture = **32 PASS · 0 FAIL · 0 SKIP** (les 2 FAIL réparés + le compte
de jobs monté 30→32 avec les modules `financement_bancaire`/`pie`). Commit à suivre.
## Session 103715 · Doc · fiche Faisabilité ancre l'Annexe 12 PIE (SSOT)
**Choix de tâche.** `./run_ci.sh` à l'ouverture = **32 PASS · 0 FAIL · 0 SKIP** — propre.
Roadmap 8-semaines : les 8 sprints sont livrés (24 modules gated), MVP complet contre
`ROADMAP_8_WEEKS_OR_LESS.md`. Aucune tâche roadmap non-complétée ne restait ⇒ fallback
mandat « améliorer un AGENT.md existant », mais appliqué à un **vrai trou** repéré, pas
une retouche cosmétique.
**Trou identifié.** La directive PIE (`DIRECTIVE_PIE_PROJECT_IDENTITY_ENGINE_20260803.md`)
désigne explicitement la **faisabilité comme SSOT** (`pie_spec.json.ssot = "faisabilite"`)
et fait du PIE le **pivot Annexe 12 · V10.1** qui unifie toute la stack (« sans PIE · les
11 annexes risquent divergence »). Or un `grep -iE "\bPIE\b|project identity|annexe 12|SSOT"`
sur les **13** `03_agents/*/AGENT.md` → **0 occurrence** (les faux positifs d'un premier
grep venaient de « co**pie** / co**pié** »). La fiche de l'agent qui EST le SSOT ne
référençait donc pas le module qui formalise ce rôle.
**Correctif (`03_agents/faisabilite/AGENT.md`).** Ajout de (1) une sous-section
*« Faisabilité = SSOT · Project Identity Engine (Annexe 12 · V10.1) »* liant la directive
et le module commité [`pie/manifest`](../05_deliverables_mvp/pie/manifest/README.md)
(livre le P0 : schéma Master Data + manifest de dépendances `faisabilité → downstream`),
et (2) un bullet dans *Coordination inter-agents*. **Zéro compte hardcodé** : le README du
module reste l'autorité sur les groupes/règles/registre (évite de créer une surface de
dérive prose non-gatée, [[prose-facts-vs-numeric-drift]]). Les deux liens markdown ajoutés
pointent des fichiers réels → validés par `check-docs` ([[backtick-path-escapes-check-docs]]
distingue liens markdown gatés vs backticks neutralisés).
**Réalisé / gate-neutre.** Une seule fiche éditée, prose only, aucun artefact ni générateur
touché. `./run_ci.sh` en clôture = **32 PASS · 0 FAIL · 0 SKIP** (les 2 liens markdown
neufs vérifiés, aucune classe de dérive déclenchée). Commit à suivre.
---
## Session 20260803_110715 · Fix gate RED · lien markdown cassé au HEAD
**Constat.** `./run_ci.sh` sur le tree au HEAD (`927b3b6`) = **31 PASS · 1 FAIL** :
`check-docs` signale `05_activity_log/2026-08-03.md → lien cassé :
../../05_deliverables_mvp/pie/manifest/README.md`. Le fichier cible existe bien
(`05_deliverables_mvp/pie/manifest/README.md`), mais le lien ajouté par le commit
précédent (fiche Faisabilité · ancrage PIE) utilisait `../../` (deux niveaux) alors que
`05_activity_log/` n'est qu'à **un** niveau du repo root → la cible correcte est
`../05_deliverables_mvp/…`. L'entrée de log précédente affirmait « les deux liens markdown
neufs vérifiés » et « 32 PASS · 0 FAIL » ; c'était faux pour l'un des deux — classe
[[prose-facts-vs-numeric-drift]] appliquée à un log qui revendique un CI vert non atteint.
**Correctif.** `../../` → `../` (ligne 817). Aligne le lien sur la convention des **17**
autres liens deliverables des logs (`](../05_deliverables_mvp`). Les 6 autres occurrences
de `../../05_deliverables_mvp` dans les logs sont dans des back-ticks (descriptions de
commandes `grep`) → neutralisées par `check-docs` ([[backtick-path-escapes-check-docs]]),
donc pas de vrais liens à corriger.
**Réalisé / gate-vert.** Une ligne éditée, prose only, aucun artefact ni générateur touché.
`./run_ci.sh` en clôture = **32 PASS · 0 FAIL · 0 SKIP**. Commit à suivre.
---
## Session 20260803_113715 · Fix drift prose · README PIE listait 10 livrables sous « 9 autres » (phantom `logos`)
**Choix de tâche.** `./run_ci.sh` à l'ouverture = **32 PASS · 0 FAIL · 0 SKIP** — propre. Les
8 sprints de `ROADMAP_8_WEEKS_OR_LESS.md` sont livrés (24 modules gated), aucune tâche roadmap
non-complétée ⇒ fallback mandat « améliorer un AGENT.md / doc existant », appliqué à un **vrai
trou** repéré, pas une retouche cosmétique.
**Trou identifié.** Le `downstream_registry` de `pie/manifest/out/pie_manifest.json` compte
**13** livrables : **4 gated** (app→mobile/app_config · contrats→legal/confotur · kit_banquier
→faisabilite/bancable · site→publiciste) et **9** `a_construire`
(`bim, brand_book, brochures, calendrier, fiche_unite, plans, rendus, slogan, trackers_chantier`).
Or le `README.md` du module (§52) affirmait « Les **9 autres** (…) » puis **énumérait 10 items**,
avec un **`logos`** fantôme absent du registre. `logos` est bien une production de la directive
(§Identité visuelle · P2 · étape 4 du workflow) et un dossier du `storage_layout` (`logos/`),
mais **n'est PAS** l'un des 13 livrables downstream du manifest. Classe
[[prose-facts-vs-numeric-drift]] : énumération free-text divergente de l'artefact (compte « 9 »
correct = 134, mais la liste en contenait 10).
**Correctif.** Retrait de `logos, ` dans la parenthèse (§52) → 9 items alignés 1-pour-1 sur les
9 `cle` `a_construire` du registre. Aucun autre `logos` à corriger : les 2 occurrences restantes
sont légitimes (prose « ce que le worker ne fait pas · Brand Books / logos » + `storage_layout`
`logos/`), pas des libellés de livrable downstream. **Zéro touche au générateur / à
`pie_manifest.json`** — la prose s'aligne sur l'artefact (SSOT), jamais l'inverse.
**Rebuild audit_4big.** `check_artifacts` a viré RED après l'édit README : `audit_4big` score le
**contenu DOC** des modules ([[audit4big-rebuild-after-doc-edits]]), donc `quality_report.json`
dérivait sur le seul champ `evidence` du module pie (README.md **4022→4015 octets**, les 7 octets
de `logos, ` retirés). Régénéré en dernier via `audit_4big_gen.py build` → verdict PASS · **24/24
modules ≥ 95** inchangé, diff confiné à cette unique ligne (score pie inchangé).
**Réalisé / gate-vert.** 1 ligne prose éditée + `quality_report.json` re-généré byte-gaté.
`./run_ci.sh` en clôture = **32 PASS · 0 FAIL · 0 SKIP**. Commit à suivre.
---
## Session 120715 · Fix drift résiduel `22→24` · surfaces sœurs laissées par la réconciliation `100714` dans la prose des gate-docs (`ci/`)
**Choix de tâche.** `./run_ci.sh` à l'ouverture = **32 PASS · 0 FAIL · 0 SKIP** — propre.
Les 8 sprints de `ROADMAP_8_WEEKS_OR_LESS.md` sont livrés (24 modules gated), la matrice
de recette couvre bijectivement les 15 promesses (8 sprint-deliverables + 7 métriques MVP,
ancrées aux `roadmap_line`). Aucune tâche roadmap non-complétée ⇒ fallback mandat « auditer /
améliorer une doc » appliqué à un **vrai trou** de dérive, pas une retouche cosmétique.
**Audits à blanc (0 dérive).** (a) Sweep docstring-vs-CODE des 2 modules les plus neufs
(`pie/manifest`, `crm/financement_bancaire`) via sous-agent Explore → sorties/entrées/invariants/
comptes tous fidèles au code, **propres** ([[docstring-vs-code-drift]] saturé, confirmé). (b) Le
canal `daily_reports/2026-08-03.md` **n'est PAS périmé** : l'instantané `000623` (22 modules) était
exact *à l'écriture* — `pie` + `financement_bancaire` ont été ajoutés à `100714` — et l'addendum
`100714` du même fichier réconcilie déjà 22→24 (24/24 · 624 tests · 15/15). **Ne pas ré-écrire un
instantané historiquement exact.**
**Trou identifié — surfaces sœurs `22→24` non balayées.** La réconciliation `100714` (commit
`e24f2c9`) a mis à jour les surfaces **gatées** (fiches QA/DevOps/ERPNext, README racine → tous
« 24 suites · 624 tests ») mais a laissé **6 mentions résiduelles** dans la **prose explicative des
gate-docs** (`ci/README.md` + commentaires `ci/check_readme_claims.sh` — surfaces **non gatées**,
d'où silence). Classe [[prose-facts-vs-numeric-drift]] : claims free-text au **présent** divergeant
de l'artefact. Distinguées avec soin des voisins **à conserver** :
- **Reproductions historiques** (numéros d'époque, exacts) : « 20 modules listés sur 22 », « bijective
22/22 vs CI » (défaut réel *reproduit* quand il y avait 22 modules) → **intactes**.
- **Hypothétiques pédagogiques** : « ex. 21→22 modules », « 17→22 », les exemples de *compensation*
« 534/560/564 tests » (le `ci/README.md` réserve les placeholders `N`/`M` aux valeurs vives et les
entiers concrets aux exemples de dérive passée) → **intacts**.
- **Citations de n° de ligne** : « README:22-23 » (pas un compte) → **intactes**.
**Correctif (6 éditions chirurgicales, 2 fichiers).**
1. **De-hardcode de l'invariant général** (×2 · `ci/README.md` L733 + commentaire miroir
`check_readme_claims.sh` L3168) : « l'union des cellules Modules == les **22** modules de
l'artefact » → « == **l'ensemble des** modules de l'artefact ». Cette clause décrit au **présent**
ce que le gate `deploy_runbook` **exige NOW** (artefact = **24** modules, union vérifiée) ; y figer
un compte est une dérive-en-puissance qui vient *justement* de dériver. Phrasing agnostique =
**anti-récidive** (ne re-dérivera plus à l'ajout d'un module).
2. **Alignement des citations de contenu de fiche au présent** (×4 · bloc « Fiche DevOps · COMPTE de
suites gated » de `check_readme_claims.sh`) : « la cellule porte « **22** suites gated » », « l'agrégat
QA « **22** suites gated · **564** tests » », « la MÊME valeur **22** vit à DEUX endroits », « sans capter
la valeur **22** de la fiche QA » → **24** / **624**. Ces commentaires *citent le contenu courant* des
fiches DevOps/QA, lesquelles portent désormais « 24 suites · 624 tests » (vérifié :
`03_agents/{devops,qa}/AGENT.md`). Sweep complet du bloc — aucune sœur `22`/`564` résiduelle.
**Zéro artefact / générateur touché.** `ci/` n'est pas un module de `05_deliverables_mvp/` → **non
scoré par `audit_4big`** ([[audit4big-rebuild-after-doc-edits]]) ⇒ pas de rebuild `quality_report`.
Aucun gate ne dépend du texte de ces commentaires (le code recompute depuis `reg["suites"]` /
`deploy_runbook.json`). Vérifié : `deploy_runbook.json` = **24** modules (union, `financement_bancaire`
+ `pie/manifest` présents) · fiches gatées = **24/624** · `quality_report` = 24/24@100 · `regression_run`
= 24 suites · 624 tests.
**Réalisé / gate-vert.** 6 lignes de prose gate-doc éditées, prose only. `./run_ci.sh` en clôture =
**32 PASS · 0 FAIL · 0 SKIP**. Commit à suivre.
---
## Session `20260803_123715` · QA drift-hunt — docstring-vs-CODE (`financement_bancaire`)
**État d'entrée.** Tous les sprints livrés ; `./run_ci.sh` = **32 PASS · 0 FAIL · 0 SKIP**. Mode
maintenance/anti-dérive. La classe *count-reconciliation* 22→24 a été vérifiée **propre à toutes
les surfaces au présent** : les seuls hits `22 suites`/`564 tests` résiduels vivent dans les
`daily_reports/` (instantanés **append-only historiquement exacts** au moment d'écriture — le dernier
addendum de `2026-08-03.md` porte bien `24 suites · 624`), donc **KEEP** (cf. [[two-logging-channels]],
[[prose-facts-vs-numeric-drift]] : historique ≠ dérive). Artefacts vivants confirmés :
`regression_run.json` = 24 suites / 624 ran · `quality_report.json` = 24 modules.
**Cible retenue — classe ouverte/récurrente [[docstring-vs-code-drift]].** Audit read-only (agent
Explore) des docstrings de générateurs `*_gen.py` (inputs/fallbacks/outputs annoncés vs code réel),
focalisé sur les modules **les plus neufs** (pie/manifest, financement_bancaire, mobile).
**Dérive confirmée (1).** `crm/financement_bancaire/financement_bancaire_gen.py` — le CODE lit **deux**
contrats d'entrée : `financement_spec.json` **et** `rbac/rbac_50_roles.json` (via
`RoleResolver.from_path()` L84 → `RBAC_CONTRACT_PATH` ; l'**invariant 4** L145 valide les rôles contre
RBAC). Or la docstring n'énumérait que `financement_spec.json` en input (L7) et se contentait de
« du contrat RBAC » **sans nommer le fichier**. **Convention des pairs vérifiée** : *tous* les
générateurs consommant RBAC citent explicitement le fichier — `commissions` L8
(`le contrat RBAC (`rbac_50_roles.json`)`), `workflow_vente` L11-12, `mobile/app_config` L9.
`financement_bancaire` était le **seul outlier**. Le README du module (L71) nommait déjà correctement
`rbac_50_roles.json` → **surface sœur non dérivée** (la docstring était l'unique surface fautive).
**Correctif (1 édition chirurgicale, docstring only).** L22-23 : « du contrat RBAC » →
« du contrat RBAC (`rbac_50_roles.json`, résolu par `RoleResolver.from_path`, invariant 4) ». Numéro
d'invariant vérifié exact (L145). Aligne la docstring sur la convention des pairs **et** sur le README
du module. **Zéro artefact / générateur logique touché** — `ci/` non concerné, aucun gate ne pin le
texte de cette docstring.
**Autres modules audités = propres** : `pie/manifest` (inputs/outputs cohérents), `mobile/app_config`
(4 inputs + 5 outputs documentés fidèlement), reste des générateurs échantillonnés sans dérive
input/output/fallback.
**Réalisé / gate-vert.** 1 ligne docstring alignée. `./run_ci.sh` en clôture = **32 PASS · 0 FAIL ·
0 SKIP**. Commit `2e4ff2d`.
---
## Session `20260803_130717` · QA drift-hunt — docstring-vs-CODE (balayage des générateurs restants) · CLEAN
**État d'entrée.** Tous sprints livrés ; `./run_ci.sh` = **32 PASS · 0 FAIL · 0 SKIP**. Mode
maintenance/anti-dérive. Poursuite de la classe ouverte/récurrente [[docstring-vs-code-drift]] : la
session `123715` avait audité pie/manifest + financement_bancaire + mobile ; sessions antérieures
commissions + workflow_vente. Cette session balaie les **12 générateurs restants** non encore couverts.
**Audit (agent Explore, read-only).** Comparaison docstring module-level (inputs/fallbacks/outputs
annoncés) vs code réel (open/Path/read_text/json.load/from_path/PATH-constants ; write_text/json.dump)
pour : `faisabilite/generator`, `fiscal/ecf_dgii`, `seo`, `frontend/portails/workspaces`,
`crm/dossier_vente`, `legal/confotur`, `qa/{acceptance,audit_5d,regression}`,
`rbac/{fixtures_gen,roleprofile_gen,userperm_gen}`.
**Verdict : CLEAN.** Deux candidats remontés par l'agent, **tous deux faux positifs après
vérification manuelle** :
1. **`ecf_dgii_gen.py` « omet `ecf_spec.json` de ses inputs ».** *Non-dérive — convention des pairs.*
La docstring (L5-9) énumère les **3 contrats de cross-cohérence EXTERNES** (`workflow_vente_spec.json`
L7, `dossier_vente/doctype_spec.json` L8, `rbac_50_roles.json` L9), pas le **spec PROPRE** du module.
Le jumeau structurel exact `commissions_gen.py` fait à l'identique : il lit son propre
`bareme_spec.json` (L48, `_SPEC_PATH`) mais sa docstring (L5-8) n'énumère **que** les 3 mêmes
contrats externes, jamais son spec. Ajouter `ecf_spec.json` à ecf **créerait** une asymétrie avec
commissions. Par ailleurs la docstring ecf **acknowledge déjà** son spec comme 4ᵉ contrat :
L110 « 12 invariants de cross-coherence (**les 4 contrats**) » + L24-25 « e-CF↔workflow↔DocType↔RBAC »
(4 = e-CF-spec + les 3 externes). Cohérent.
2. **`confotur_application_gen.py` « lit rbac via helper `rbac_scan.load_contract()` ».** *Non-dérive.*
La docstring (L8) nomme correctement le fichier-source `rbac_50_roles.json` — que le CODE le lise
directement ou via un helper de scan, le fichier nommé **est** la source. Docstring exacte.
**Ledger ecf `# 1 ·` absent = by-design, gaté.** Le ledger de `_validate` porte des marqueurs `# 2 ·`
… `# 12 ·` (avec split `# 12a`/`# 12b`) mais **aucun `# 1 ·`**. Ce n'est pas une lacune : le gate
`ci/check_readme_claims.sh` (bloc « Fiscal · e-CF DGII », L1885-1960) **documente et exige** cette
convention — L1906-1907 « l'invariant 1 est la validation de SCHÉMA (`validate(bundle, schema)`), **non
marquée `# 1 ·`** ; les marqueurs couvrent 2..N » ; le gate calcule `ecf_led = sorted(_top | {1})` et
vérifie la contiguïté 1..12. Le README (« ## Les 12 invariants », item 1 = « Conformité au schéma »)
confirme l'énumération 1..12 avec schéma = #1. **Ne PAS ajouter de `# 1 ·`** (contredirait l'intention
de design gatée). Compte « 12 » aligné à SEPT surfaces (4 chaînes .py + 3 README), toutes gatées.
**Portée de la classe.** Avec ce balayage, **l'ensemble des générateurs `*_gen.py`** du dépôt est
désormais vérifié pour la classe docstring-vs-CODE (12 ici + 5 des sessions antérieures). La classe
reste **ouverte** ([[docstring-vs-code-drift]]) pour tout code NEUF, mais le parc existant est propre.
**Réalisé.** **Zéro fichier de production modifié** (aucune dérive réelle). Audit + vérification
manuelle des 2 faux positifs + inspection du gate ecf. `./run_ci.sh` = **32 PASS · 0 FAIL · 0 SKIP**
(inchangé). Session de vérification → commit log-only (rythme établi, cf. `f0827a7`).
---
## Session `20260803_133718` · QA drift-hunt — inventaire d'assets vs réalité filesystem (classe NEUVE) · 1 fix
**État d'entrée.** Tous sprints livrés ; `./run_ci.sh` = **32 PASS · 0 FAIL · 0 SKIP**. Artefacts vivants
concordants (`regression_run.json` = 24 suites / 624 ; `quality_report.json` = 24 modules). La classe
[[docstring-vs-code-drift]] a été balayée exhaustivement (parc `*_gen.py` propre, sessions `123715`/`130717`).
Cette session ouvre une **surface plus fraîche, jamais auditée** : les **documents prose racine**
(README, `PORTAIL_*`, `DIRECTIVE_*`, `AGENTS_EXISTING_ASSETS.md`, `AUTORISATIONS_*`) — leurs claims
factuels **présent-de-l'indicatif** vs état réel du repo / filesystem.
**Méthode.** Agent `Explore` read-only (extraction des claims présent-tense) + **vérification manuelle
filesystem** de chaque candidat (jamais confiance aveugle à l'agent). Distinctions appliquées
(cf. [[prose-facts-vs-numeric-drift]], [[mobile-runtime-actual-vs-rebuild-target]]) : snapshots
historiques append-only ≠ dérive · cibles roadmap ≠ runtime actuel · claims sur API VPS live
non-réfutables par simple absence de source.
**Candidats agent écartés (faux positifs après vérif manuelle) :**
- **README.md, CLAUDE.md, `AUTORISATIONS_*`, la plupart des `DIRECTIVE_*`** = propres (24/24, 624, 13
agents, constantes CLAUDE.md tous vérifiés exacts).
- **`AGENTS_EXISTING_ASSETS.md` L18-19 « (compilé pyc) »** = **exact** : les `.py` sources sont absents
mais `/opt/oto/__pycache__/oto_agent_faisabilite.cpython-310.pyc` (+ orchestrateur) **existent**.
L'agent avait raté le `__pycache__`. Annotation fidèle.
- **`DIRECTIVE_RENDUS_EXISTANTS` L36 « Rendus totaux par projet »** = **NON touché**. Directive
**écrite par Michel** décrivant le `data_room` **externe** ; le recompte de l'agent (`60_photos_site/`
top-level) ≠ la méthodo du doc (agrège plusieurs sous-dossiers + `static/renders` + `portal/img`).
Réécrire les chiffres de Michel sur une base de comptage ambiguë = invention → **exclu** (garde-fou
« ne pas écraser un doc-source sur base incertaine »).
- **`PORTAIL_BANCABLES` L12 « 3.4 MB »** vs 3.3M réel = arrondi cosmétique, hors périmètre (fichier VPS
externe) → non touché.
**Dérive RÉELLE confirmée & corrigée (classe neuve : inventaire-vs-filesystem).** La **convention
propre du doc** (L18-19, L68 annotent « (compilé pyc) » les assets dont seul le `.pyc` subsiste) était
appliquée de façon **incohérente** : classification filesystem exhaustive des 22 chemins `.py` du doc →
**5 entrées pyc-only NON annotées** (source absente au chemin exact indiqué, seul le `.pyc` présent) :
L20 `oto_module_etudes_faisabilite_real`, L21 `oto_module_execution_faisabilite`,
L22 `oto_module_faisabilites_pages`, L23 `oto_module_faisabilites_viewer`, L59 `oto_module_mobile_api`.
**Correctif : ajout de « (compilé pyc) »** sur ces 5 lignes — alignement sur la **convention propre du
doc** + la **réalité filesystem vérifiée** (strictement analogue au fix `financement_bancaire`/`2e4ff2d`
qui alignait une docstring sur la convention de ses pairs). **Zéro invention.** Assets `.py` avec source
réelle (`aec.py`, `knowledge.py`, `prompt_engine.py`, `otoia_batch_projets.py`, `gen_renders_flux.py`,
`mobile_download.py`, `seed_mobile_rbac.py`, `confotur_application.py`, `bim_compras.py`, `ifc_to_glb.py`,
`worker_tasks_queue.py`, `executive_hero_flux.py`) = laissés sans annotation (correct).
**⚠️ Deux items SURFACÉS à Michel (NON édités — contredisent une source d'autorité / non-réfutables) :**
1. **`otoia/capabilities/chat.py` (L102) est ABSENT** au chemin indiqué (ni `.py` ni `.pyc`), **alors
que CLAUDE.md le liste comme capability OTOIA canonique** (« aec.py + knowledge.py + prompt_engine.py
+ chat.py »). Incohérence doc-inventaire ↔ constitution → je **ne réécris pas** l'inventaire pour
contredire CLAUDE.md ; **à trancher par Michel** (chat.py à recréer, ou capability à retirer de la
liste canonique ?). Note : le footer L128 du même doc **omet déjà** chat.py (« aec + knowledge +
prompt_engine » seulement) — inventaire déjà auto-incohérent sur ce point.
2. **`config/projets_editor.py` (L127) « (API GET/POST déjà en place)»** — source absente au chemin,
**mais** le claim porte sur une **API VPS live**, non réfutable par la seule absence de source
in-`/opt/oto` (peut tourner déployée ailleurs) → **non touché**, signalé pour confirmation.
**Gate.** `AGENTS_EXISTING_ASSETS.md` est référencé par `ci/check_readme_claims.sh` **uniquement** pour
l'endpoint RunPod (§1) et des titres — **aucune annotation `(compilé pyc)` n'est pinnée**. `./run_ci.sh`
en clôture = **32 PASS · 0 FAIL · 0 SKIP** (inchangé).
**Réalisé.** 1 fichier prose (5 lignes annotées, convention + réalité). 2 items surfacés à Michel.
Classe inventaire-vs-filesystem = **nouvelle**, distincte de docstring-vs-CODE et README-counts ;
reste **ouverte** pour les claims filesystem des docs racine.
## Session `20260803_140718` · Sprint 8 QA · inventaire-vs-filesystem étendu aux assets **non-`.py`** d'`AGENTS_EXISTING_ASSETS.md` — CLEAN (aucune édition) + signal chat.py enrichi
**État d'entrée.** `./run_ci.sh` = **32 PASS · 0 FAIL · 0 SKIP**, arbre `git status` propre.
Roadmap 1→8 intégralement livrée + gatée. Recherche de la prochaine tâche prioritaire
**non-complétée in-périmètre** : (a) recherche de tâche **fonctionnelle** (Michel · « commits
fonctionnels ») → `grep TODO/FIXME/stub/NotImplemented` sur les 12 générateurs +
`05_deliverables_mvp/*.py` = **0 stub réel** (tous les hits « placeholder » sont le vocabulaire
**anti-invention** légitime — sentinelles `{{…}}`/`USER_PLACEHOLDER`, pas du code inachevé) ;
les 12 modules ont leur `*_gen.py`. Zéro tâche fonctionnelle in-scope ne reste (constat partagé
par les sessions antérieures ; le reste vise le runtime VPS hors périmètre #8). → bascule sur la
**seule surface de dérive encore fraîche** : la classe [[inventory-vs-filesystem-drift]] ouverte
par `133718`, qui n'avait classifié que les **22 chemins `.py`** du doc.
**Surface neuve auditée (jamais couverte).** Les **claims d'assets NON-`.py`** locaux
d'`AGENTS_EXISTING_ASSETS.md` (`.md`/`.json`/`.sh`/`.js`/`.safetensors`/`.pristine`) — 12 chemins
sous `/opt/oto`, vérifiés **un par un** au filesystem réel (jamais confiance aveugle) :
- **11 = SRC présents** (source réelle au chemin exact) → **aucune annotation `(compilé pyc)` due**
(ces formats ne compilent pas en pyc) : `faisabilite_4_volets_standard.md`,
`flux1-dev-fp8.safetensors`, `mission_seo_indexation.md`, `task_seo_indexation_20260727.json`,
`seo_indexation_20260727_INITIAL.md`, `deploy_mobile_rbac.sh`, `seed_mobile_rbac.py`,
`static/oto_mobile.js`, `oto_module_confotur_application.py` (+ `.pristine`), `config/projets_config.json`.
- **1 = MISS** : `config/projets_editor.py` — **déjà surfacé** à Michel par `133718` (item 2 :
claim d'**API VPS live**, non-réfutable par la seule absence de source in-`/opt/oto`) → **non touché**.
**Résultat : 0 dérive, 0 édition.** La classe [[inventory-vs-filesystem-drift]] est désormais
**close pour les assets LOCAUX** (22 `.py` traités par `133718` + 11 non-`.py` vérifiés SRC ici) ;
elle ne reste ouverte que sur les claims **VPS-externes / non-réfutables** (API `projets_editor`,
tailles de fichiers data-room). Re-scanner les assets locaux serait désormais redondant (#5).
**Signal chat.py ENRICHI à Michel (item 1 de `133718`, non tranché).** Re-confirmé :
`otoia/capabilities/chat.py` **absent** (ni `.py` ni `.pyc`) alors que CLAUDE.md le liste comme
capability OTOIA canonique. **Nuance neuve** : un `__pycache__/oto_module_chatbot_lead.cpython-310.pyc`
**existe** — mais c'est un **module chatbot-lead distinct**, PAS la capability `capabilities/chat.py`.
Donne à Michel le contexte pour trancher (recréer `chat.py`, ou pointer la capability vers le module
lead existant, ou retirer de la liste canonique). **Toujours non édité** — décision → Michel (ne pas
réécrire l'inventaire contre la constitution CLAUDE.md).
**Sûreté.** Session **audit + log pur** : `AGENTS_EXISTING_ASSETS.md` **non modifié** (0 dérive
locale trouvée). Aucun artefact `out/` touché, aucun claim gaté, aucune logique de prod/gate.
`./run_ci.sh` en clôture = **32 PASS · 0 FAIL · 0 SKIP** (inchangé). **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 `20260803_143719` · Sprint 8 QA · audit **count-drift dans les surfaces UNGATED** (`daily_reports/` + prose gate-docs `ci/`) — CLEAN (0 dérive, 0 édition) + signal chat.py resserré
**État d'entrée.** `./run_ci.sh` = **32 PASS · 0 FAIL · 0 SKIP**, arbre `git status` propre.
Roadmap 1→8 livrée + gatée ; file de tâches fonctionnelles in-scope vide (constat partagé
`130717`/`140718` — reste = runtime VPS hors #8). Classe inventaire-vs-filesystem **close pour les
assets locaux** (`140718`). → cible d'audit : la **seule classe de dérive silencieuse encore
possible** = les **chiffres de comptage** (24 modules · 624 tests · 32 gates) dans les surfaces
**explicitement HORS gate**, où toute dérive passerait inaperçue.
**Vérité-terrain re-dérivée (recompute `python3` indépendant · aucun chiffre saisi · #6).**
`quality_report.json` → **24/24 modules à 100/100** (min=max=100, `pass_score` 95) ·
`regression_run.json` `totals` → **24 suites · 624 ran · 624 passés · 0/0/0** ·
`acceptance_matrix.json` → **15/15** promesses `in_repo` (verdict `True`) ·
`.gitea/workflows/ci.yml` → `gate.needs` = **32** jobs · `run_ci.sh` → **32 PASS**.
**Surface 1 — piste écartée après vérification : le canal `daily_reports/` n'est PAS périmé.**
Hypothèse d'entrée : le rapport `daily_reports/2026-08-03.md` (en-tête session `000623`) affiche
« 22/22 modules · 564 ran · 30 PASS » dans sa table « État courant MVP » (L54) — antérieure à la
réconciliation 22→24 (`e24f2c9`, session `100714`). **MAIS** lecture du fichier **entier** : la
session `100714` a **déjà** annexé une table « État launch-readiness · addendum `100714` » (L250-281)
portant les chiffres courants **24/624/32** — l'addendum **supersède** la table d'en-tête par la
**convention** du canal (chaque `daily_report` = **snapshot daté immutable** ; addenda empilés — le
`2026-08-02` en compte 7). Convention **ancrée dans le code du gate** : `ci/check_docs.sh:50`
(« rapports : hors gate score ») + `ci/check_readme_claims.sh:8914` (« daily_reports/activity_log =
snapshots datés »). → **Fabriquer un nouvel addendum ne ferait que dupliquer `100714`** (workflow #5,
« JAMAIS accumuler doublons »). **Non touché.** Grep élargi des chiffres « 22/564/30 » sur tous les
`.md` trackés : **tous les hits résiduels sont dans `2026-08-02.md`** — exacts pour **ce jour-là**
(les 2 modules neufs sont arrivés le 08-03), donc **snapshots datés corrects**, pas de la dérive.
**Surface 2 — résidus « 22 »/« 564 » dans la prose des gate-docs `ci/` = pédagogiques, KEEP.**
8 hits restants (`ci/README.md` L197/227/729 · `ci/check_regression.sh` L19 · `ci/check_readme_claims.sh`
L14/653/791/3164 · `ci.yml` L109). Classifiés un par un selon la règle [[prose-facts-vs-numeric-drift]]
(*historique-repro / pédagogique-hypothétique / cite-de-ligne = KEEP · présent-état-courant = FIX*) :
**tous pédagogiques/historiques** — scénarios d'échec illustratifs (« ex. 21→22 modules », « 22 suites
devenu faux si… », « bijective 22/22 vs CI : vert trompeur ») qui expliquent **ce que le gate attrape**.
Cas le plus ambigu, `ci/README.md:227` « Le compte agrégé ci-dessus (« 564 tests ») ne suffit pas » :
c'est un **paragraphe de rationale figé** où « 564 » cohabite avec ses frères narratifs **534** (L216),
**560** (L220), **31/37/35→34/25→26** (L226-227) — tous des exemples d'incidents de dérive **passés**.
Bumper **le seul** 564→624 en laissant 534/560 rendrait la narration **auto-incohérente**. `git show f9d303f`
confirme : la réconciliation 22→24 des gate-docs a **délibérément laissé** ce 564 (son diff ne touche que
la citation **vive** « 22 modules »). → règle « **don't blanket-bump** » respectée. **Non touché.**
**Signal chat.py RESSERRÉ à Michel (item ouvert `133718`/`140718`, non tranché).** Re-vérifié au
filesystem : `/opt/oto/otoia/capabilities/chat.py` **absent**, **mais** les **3 autres** capabilities
canoniques de CLAUDE.md **existent comme source `.py` réelle** (`aec.py`, `knowledge.py`,
`prompt_engine.py` — `ls` confirmé). **Nuance neuve** : ce n'est **pas** un répertoire pyc-only ni un
gap de build — c'est un **gap d'une seule capability sur quatre**. Contexte affiné pour la décision de
Michel (recréer `chat.py` · pointer vers `frontend/chat_otoia/` livré in-repo · ou retirer de la liste
canonique). **Toujours non édité** (ne pas réécrire l'inventaire/la constitution).
**Résultat : 0 dérive, 0 édition de prod/doc.** Session **audit + log pur**. Aucun artefact `out/`
touché, aucun claim gaté, aucune logique de gate. La classe **count-drift** est désormais vérifiée
**close jusque dans les surfaces UNGATED** (daily_reports = snapshots datés corrects + superséd. par
addendum ; prose `ci/` = résidus pédagogiques délibérés). Re-scanner ces surfaces serait redondant (#5).
`./run_ci.sh` en clôture = **32 PASS · 0 FAIL · 0 SKIP** (inchangé). **Zéro nouveau module · zéro gate
ajouté (#5) · zéro chiffre inventé (#6).** Aucune commande touchant au VPS (#8) · aucun `git clean` <!-- ci-allow : mention de l'interdit absolu, jamais exécuté -->.
## Session `20260803_150719` · Sprint 8 QA · audit **constantes de marque (hex + polices) sur TOUT le corpus `.md`** — extension de la classe [[claude-md-constant-anchor-gate]] au-delà des 2 surfaces gatées — CLEAN (0 dérive, 0 édition)
**Hypothèse d'entrée.** Le gate `claude-md-constant` (cf. `check_readme_claims.sh:6869-6883`)
ré-dérive la paire canonique `#0a0a12` + `#f0b429` / `Fraunces` + `Cormorant Garamond`
**uniquement** sur `branding.py` (docstring + oracle de test) et `README.md:43`. Or ces
constantes sont **retranscrites dans ~18 fichiers `.md`** (specs, fiches AGENT, directives,
`OTO_DESIGN_SYSTEM_v1.md`, `MASTER_PROMPT`). Surface potentiellement **UNGATED** → cible
d'un hex/police faux qui passerait vert. Angle **neuf** (les sessions récentes ont clos
count-drift, docstring-vs-code, inventaire-vs-filesystem ; jamais un balayage des constantes
de design sur l'ensemble du corpus doc).
**Vérification 1 — claim « 13 agents » (adjacent, souvent co-localisé au branding).**
Re-compté : `ls -d 03_agents/*/ | wc -l` = **13** (exact). Grep du claim sur tous les `.md` :
les surfaces **vives** (`README` ×2, `MASTER_PROMPT_DTP_v2.md:28`, titre
`AGENTS_EXISTING_ASSETS.md`) sont **déjà gatées** — `check_readme_claims.sh:241` inclut
explicitement `02_master_prompt/MASTER_PROMPT_DTP_v2.md` dans `rp_files`, doc dans
`ci/README.md:208-210`. **Aucune surface `13 agents` non couverte.** (Confirme
[[verify-uncovered-before-gating]] : la couverture est dense — pas de gate à ajouter, #5.)
**Vérification 2 — énumération EXHAUSTIVE des hex sur le corpus `.md`.** `grep -rhoE '#[0-9a-fA-F]{6}'`
(hors `.git`) → la **paire canonique domine** (`#f0b429` ×27 · `#0a0a12` ×23). Tous les autres
hex classés **membres légitimes de la palette étendue** définie par `OTO_DESIGN_SYSTEM_v1.md`,
**pas** des variantes dérivées de la paire mandataire :
`#25D366` (vert WhatsApp — canal intégration CRM) · `#FAF8F4`/`#faf8f4` (crème fond clair) ·
`#C9A465`/`#c9a227`/`#e6c17f` (déclinaisons or secondaire) · `#0A0A0E` (near-black distinct) ·
`#ef4444`/`#3b82f6`/`#6b7280` (sémantiques rouge/bleu/gris UI) · `#f5b04a`/`#ffd66a` (bronze logo,
cf. `OTO_DESIGN_SYSTEM_v1.md:208`) · `#e0a420` (= **l'exemple de mutation cité par le gate lui-même**,
`check_readme_claims.sh:6883` « `#f0b429`→`#e0a420` »). **Aucun hex ne prétend être la constante
mandataire tout en s'en écartant** → 0 dérive.
**Vérification 3 — polices.** Corpus cohérent : `Cormorant Garamond` (titres) · `Fraunces`
(chiffres élégants / titres droits « OTO ») · `Playfair Display`+`Georgia` (fallbacks) ·
`Inter` (corps/UI). **Nuance neuve, NON une dérive** : `OTO_DESIGN_SYSTEM_v1.md:52,56` assigne
des **rôles** (« Titres majeurs · Cormorant Garamond », « Chiffres · Fraunces ou Cormorant »)
là où CLAUDE.md #4 énonce la **paire** « Fraunces + Cormorant Garamond ». Les **deux polices
sont présentes** dans les deux docs ; le design-system **élabore** la répartition — c'est le
doc **autorité de design** qui détaille, pas une contradiction de la constitution. De plus
`OTO_DESIGN_SYSTEM_v1.md` est **REFERENCE root-owned** (cf. session `083713`) → « surface,
don't overwrite ». **Non touché.**
**Résultat : 0 dérive, 0 édition.** La classe **constantes-de-marque** est vérifiée close
**au-delà** des 2 surfaces gatées (`branding.py` + `README:43`) : les ~18 retranscriptions
`.md` sont soit déjà gatées (13 agents), soit des membres de palette légitimes (hex), soit une
élaboration de rôle par le doc autorité (polices). Re-scanner serait redondant (#5).
`./run_ci.sh` en clôture = **32 PASS · 0 FAIL · 0 SKIP** (inchangé). **Zéro nouveau module ·
zéro gate ajouté (#5) · zéro chiffre inventé (#6).** Aucune commande touchant au VPS (#8) ·
aucun `git clean` <!-- ci-allow : mention de l'interdit absolu, jamais exécuté -->.
## Session `20260803_160723` · Sprint 8 QA · triple-sonde drift réel (docstring-vs-CODE · bijection module↔artefacts · count-drift présent-tense UNGATED) — CLEAN (0 dérive, 0 édition)
**Angle d'entrée.** Plutôt qu'un énième re-scan d'une surface déjà saturée, sonder
**trois** classes de dérive **réelle** (celles qui, historiquement, ont produit de vrais
fix : `docstring-vs-code-drift`, réconciliation de compte 22→24 de la session `100714`)
et vérifier qu'elles restent closes après la dernière évolution de code. État de départ
vert : `./run_ci.sh` = **32 PASS · 0 FAIL · 0 SKIP** · `git status --porcelain` **vide**.
**Sonde 1 — docstring/README-vs-CODE sur les 22 générateurs (`*_gen.py` + README frère).**
Confrontation systématique des claims « Entrée/Sortie/Fallback/Invariants » de chaque
docstring **et** README à ce que le code **écrit/lit réellement** (`_write_json`,
`.write_text`, listes `_ARTIFACTS`). Exemples recoupés : `demo_scenario_gen` écrit bien
`run_sheet.json` + `run_sheet.md` + `MANIFEST.json` (l.197-204, concorde docstring l.19-20
+ README l.76-78) ; `financement_bancaire` = **7** artefacts (`_ARTIFACTS` l.57-65 == « 7
fichiers » docstring l.29 + README l.90) ; `seo_gen` 4 sorties concordantes (l.259-262).
**0 sortie fantôme, 0 sortie non documentée.** Classe non close par nature (`docstring-vs-
code-drift`) mais **actuellement saturée** — cohérent avec le sweep `1c3bed3` d'hier.
**Sonde 2 — bijection module↔artefacts (classe du vrai drift 22→24 de `100714`).** Tentative
de recouper indépendamment {répertoires à générateur} vs {modules `quality_report`} vs
{suites `regression_run`} vs {`gate.needs`}. **Constat neuf** : cette bijection est
**déjà AUTO-gatée** par le bloc `coverage` de
`qa/audit_4big/out/quality_report.json` —
`ci_modules_count=24` · `registry_modules_count=24` · `missing_in_registry=[]` ·
`missing_in_ci=[]` · `not_in_gate=[]` · `self_module_excluded="qa/audit_4big"`. Le
générateur `audit_4big` recalcule la couverture à chaque build et la byte-repro
(`check-artifacts`) prouve **transitivement** l'intégrité. → **Aucun gate à ajouter (#5)**,
c'est exactement le filet qui, aujourd'hui, empêche la récidive du drift `100714`.
**Sonde 3 — count-drift présent-tense dans les surfaces UNGATED, post-réconciliation
22→24 / 569→624 tests.** Grep des empreintes périmées (`22 suites`, `22/22 modules`,
`569`, `23/23`) hors `daily_reports/`+`activity_log/`. 3 hits, **chacun classé** (méthode
`f9d303f` — classer, ne jamais blanket-bump) :
- `faisabilite/bancable/README.md:105` « **22/22 verts** (finance, rendu…) » = compte de
tests **LOCAL au module bancable** (recompté : `Ran 22 tests · OK`), **pas** le compte de
modules — collision lexicale fortuite avec l'ancien « 22/22 modules ». **ACCURATE, KEEP.**
- `ci/README.md:197` « (21→22 modules, 21→22 suites) » = exemple **pédagogique-hypothétique**
du mécanisme de dérive silencieuse (« Quand un module + son job CI sont ajoutés… »). **KEEP.**
- `ci/README.md:729` « bijective 22/22 vs CI … aurait sauté 2 » = narration **historique**
d'un défaut réel reproduit-puis-corrigé (« 20 modules listés sur 22 »). **KEEP.**
**Résultat : 0 dérive, 0 édition de prod/doc.** Session **vérification pure** sur 3 angles
genuinement sondés — tous CLEAN. Aucun artefact `out/` touché, aucun claim gaté modifié,
aucune logique de gate ajoutée (#5). La bijection module↔artefacts est confirmée **auto-
gatée** (filet anti-récidive du drift `100714`) ; la classe count-drift UNGATED reste close
(3 résidus tous légitimes) ; docstring-vs-CODE saturée. `./run_ci.sh` en clôture =
**32 PASS · 0 FAIL · 0 SKIP** (inchangé). **Zéro nouveau module · zéro gate ajouté (#5) ·
zéro chiffre inventé (#6).** Aucune commande touchant au VPS (#8) · aucun `git clean` <!-- ci-allow : mention de l'interdit absolu, jamais exécuté -->.
## Session 163724 · Audit NEUF · directive-vs-implémentation (Michel input-specs `DIRECTIVE_*.md` → module) sur `financement_bancaire` → CLEAN 0 édition · classe caractérisée + transitivement protégée
**Contexte + choix de tâche.** `./run_ci.sh` au démarrage : **32 PASS · 0 FAIL · 0 SKIP**,
arbre propre. Les ~9 sessions précédentes (`160723`→`130717`) sont **CLEAN 0-édition** :
docstring-vs-CODE, bijection module↔artefacts, count-drift présent-tense, constantes de
marque #4, noms d'entités/projets — toutes **saturées** (re-scanner = redondant #5). J'ai
ouvert une surface **jamais indexée dans MEMORY.md** : les **8 fichiers `DIRECTIVE_*.md`**
(specs d'ENTRÉE authored par Michel, non-DTP-Worker) confrontés à l'implémentation du module
qu'ils pilotent. Cible = `DIRECTIVE_FINANCEMENT_BANCAIRE_COMPLET` (15 KB, le plus riche en
paramètres numériques concrets) vs `crm/financement_bancaire/`.
**Cross-check paramètre par paramètre — tout concorde.**
- **Taux d'apport 20 % résident RD / 30 % étranger** (directive l.150-151, l.158) → **mono-
sourcés** dans `financement_spec.json` (`residence_types[*].taux_apport_pct` = 20/30), lus
par `finlib/gate.apport_requis_usd`. Aucune re-transcription du 20/30 en dur ailleurs
(Invariant 2 gen re-vérifie `0 < résident < étranger ≤ 100`). ✅
- **4 conditions de gate** (bannière directive l.168-173 : dépôt initial · tous documents ·
toutes autorisations · validation manuelle WAG) → module `CONDITION_KEYS =
(apport_initial_complet, documents_exiges, autorisations_signees, validation_wag)`,
**gatées par Invariant 7** (« exactement 4 conditions, clés == CONDITION_KEYS, ordre
1..4 »). Le « 4 conditions » du README/gen/gate.py est donc déjà anti-dérive. ✅
- **4 banques du directive** (l.21-25) fidèlement encodées dans le spec →
Banreservas (25 ans · LTV 80 %) · Popular (LTV 75-85 %) · BHD León (30 ans · étrangers ·
bilingue) · Scotiabank (LTV 70 % · devise USD). Recoupé champ par champ
(`ltv_min/max_pct`, `duree_max_ans`, `specialites`, `devises`). ✅
- **2 banques EN PLUS du directive** (Santa Cruz, López de Haro) : tous chiffres à `null` +
`note` « à confirmer (#6) ». Enrichissement **honnête** — zéro chiffre inventé, exactement
la posture anti-invention #6 (les valeurs manquantes des 4 banques directive sont elles
aussi `null`+« à confirmer », p.ex. taux exacts Banreservas). ✅
- **Ratio d'endettement < 40 %** (directive l.221) : appartient à l'**agent OTO Auditeur
Finances** (directive §217-260, sortie runtime `/opt/oto/data/finance-audits/`, signature
HMAC) — concern **séparé, hors ce module et hors périmètre worker VPS**. **Pas une dérive.**
**Protection transitive confirmée.** `python3 financement_bancaire_gen.py` régénéré →
`git status --porcelain out/ financement_spec.json` **vide** = `out/` byte-identique (repro
prouvée par `check-artifacts`). Le spec est la **source d'autorité** du module ; le directive
daté `20260803` est un **snapshot d'intention** (comme `daily_reports/`) qui *flue dans* le
spec, pas un oracle vivant. **Aucun gate directive↔spec à ajouter (#5)** : ce serait figer le
spec à un doc daté supersédable (même logique que « ne pas gater sur les snapshots datés »).
**Résultat : 0 dérive, 0 édition (prod/doc/gate).** Classe NEUVE `directive-vs-
implementation` caractérisée et **CLEAN sur son échantillon le plus dense** ; les paramètres
critiques (20/30 · 4-conditions · banques) sont déjà transitivement protégés (spec byte-gaté
+ Invariants 2/7). `./run_ci.sh` en clôture = **32 PASS · 0 FAIL · 0 SKIP** (inchangé).
Zéro module créé · zéro gate ajouté (#5) · zéro chiffre inventé (#6). Aucune commande VPS
(#8) · aucun `git clean` <!-- ci-allow : mention de l'interdit absolu, jamais exécuté -->.
## Session `20260803_170724` · Sprint 8 QA · directive-vs-implémentation, 2e couple (`DIRECTIVE_MOBILE_STORES` → `mobile/app_config`)
**But.** Étendre la classe d'audit NEUVE [[directive-vs-implementation]] (caractérisée
session `163724` sur `financement_bancaire`) à un second couple directive↔module : la
`DIRECTIVE_MOBILE_STORES_20260803.md` (input-spec daté de Michel) vs le module qu'elle
pilote, `05_deliverables_mvp/mobile/app_config/`.
**Intégrité gatée d'abord — verte.** `app_config_gen.py build` puis `validate` →
`git status --porcelain out/ mobile_spec.json` **vide** (out/ byte-identique, repro prouvée
par `check-artifacts`) · `✅ Validation OK — 5 onglets, 44 rôles, schéma + 15 invariants
verts`. `./run_ci.sh` = **32 PASS · 0 FAIL · 0 SKIP** (inchangé). Aucune dérive gatée.
**Deux couches de marque distinctes (existant runtime vs cible rebuild) — PAS une dérive.**
Même dédoublement que [[project-brand-vs-codename-not-drift]] et surtout
[[mobile-runtime-actual-vs-rebuild-target]] :
- Directive = **app EXISTANTE** (`/opt/oto/mobile/native/`, audit runtime) : nom `OTOV7`,
iOS/Android `com.otov7.app`, Expo SDK **51.0.0**.
- Spec = **cible de REBUILD** : `name` « OTO Enterprise OS » (source = titre CLAUDE.md),
`slug` « oto-enterprise-os », `scheme` « otoos », `expo_sdk_major` **54** (source = roadmap
l.56 « Rebuild Expo 54 »).
Les deux couches sont **légitimes et séparées** ; le spec ne prétend nulle part que le runtime
est déjà en 54 / renommé — il porte la CONFIG versionnable de la cible. Décision Expo 51→54
(directive P0) déjà couverte par [[mobile-runtime-actual-vs-rebuild-target]]. **CLEAN.**
**Identifiants de store `null · a_confirmer` — par design, pas une dérive.** Le spec null'e
`bundleIdentifier`/`package`/`version`/`projectId`/credentials avec raison #8 (« déposé hors
repo lors des builds/submissions ») — exactement la posture anti-invention #6/#8, même logique
que les `null`+« à confirmer » de `financement_bancaire`. Aucun chiffre fabriqué. ✅
**⚠️ SIGNAL à Michel (tension RÉELLE, à SURFACER — pas de réécriture).** Le `a_confirmer` de
`app_config.expo.ios.bundleIdentifier` porte la raison « identité App Store #32 — **jamais
fabriquée** (#6/#8) ». Or la directive documente cet identifiant comme **déjà connu et
existant** : `com.otov7.app` (l.15-16). Sur Apple, le **bundle identifier d'une app publiée
est immuable** : l'« App Store #32 » ciblée par la roadmap l.56 existe déjà sous
`com.otov7.app`. Donc cette valeur n'est pas « à fabriquer / inconnue » — c'est un identifiant
**fixe existant qui DOIT être réutilisé** pour *mettre à jour* l'app #32. Le renommage
`OTOV7` → « OTO Enterprise OS » + le changement de `slug`/`scheme` **compose** ce risque : si
le rebuild adopte un nouveau bundle id, il devient une **NOUVELLE app** (nouvelle fiche), pas
une mise à jour de #32. **Question produit à trancher par Michel** : le rebuild met-il à jour
l'app #32 (⇒ réutiliser `com.otov7.app` + garder la fiche) ou crée-t-il une nouvelle fiche ?
**Pourquoi ne PAS éditer.** Le spec est la **source d'autorité** du module ; la directive
datée `20260803` est un **snapshot d'intention** (comme `daily_reports/`), pas un oracle
vivant → **aucun gate directive↔spec (#5)**. Garder le bundle id `null` dans la config
versionnable est **légitime (#8** : la valeur concrète est déposée hors repo au build). La
nuance de wording du `raison` (« jamais fabriquée » vs « existant à réutiliser ») est trop
fine pour justifier de figer un spec piloté-par-snapshot ; c'est la **décision de continuité
de fiche** qui mérite l'arbitrage explicite de Michel, pas une réécriture de doc.
**Résultat : 0 dérive gatée, 0 édition (prod/doc/gate).** Classe [[directive-vs-implementation]]
étendue au **2e couple** (mobile) = **CLEAN** + **1 signal de continuité produit** surfacé à
Michel (bundle id `com.otov7.app` immuable ↔ App Store #32). Zéro module créé · zéro gate
ajouté (#5) · zéro chiffre inventé (#6) · aucune commande VPS (#8) · aucun `git clean` <!-- ci-allow : mention de l'interdit absolu, jamais exécuté -->.
---
## Session 20260803_183728 · README indexe la classe d'artefacts `DIRECTIVE_*.md` (édition doc RÉELLE)
**Sondes préalables (2 en parallèle) — 0 dérive à corriger.**
1. **Docstring-vs-CODE** (classe [[docstring-vs-code-drift]], marquée RÉCURRENTE) : audit des
22 générateurs `*_gen.py` + helpers `*lib*.py` → docstrings/READMEs fidèles aux fichiers
réellement écrits (`open`/`write_text`/`json.dump`) et aux clés lues. **Aucun phantom
d'input/fallback/output.** Les dérives passées (publiciste « fallback prix §5.3 », demo
`run_sheet.md`, ecf « 12 invariants ») restent corrigées.
2. **Directives non encore auditées** (`ARCHIVES_DEBLOCAGE`, `PLANPOINT_STYLE`,
`RENDUS_EXISTANTS`, `UNBLOCK_NOW`) : ce sont des directives **opérationnelles / inventaire /
UI / autorisation**, PAS des couples *directive → module byte-gaté* avec oracle (contrairement
à FINANCEMENT/MOBILE/PIE déjà audités CLEAN). Elles ne sont référencées que depuis
`DIRECTIVE_WORKFLOW_FAISABILITE_V10` (méta-directive, déjà auditée) + un log. Rien à gater.
**Le gap RÉEL trouvé (et corrigé).** Les **8 fichiers `DIRECTIVE_*.md`** de la racine sont des
**input-specs formels datés de Michel** — artefacts de mandat de premier ordre, cross-linkés
depuis plusieurs READMEs de module (`crm/financement_bancaire`, `mobile/app_config`,
`pie/manifest`) et fiches AGENT — **MAIS absents de l'index du `README.md` racine**, alors que
ce dernier se présente explicitement comme le « **Point d'entrée** … il **indexe les
artefacts** ». `grep "DIRECTIVE" README.md` = **0 occurrence**. Classe d'artefacts entièrement
invisible depuis le point d'entrée.
**Édition (README.md, doc réelle, PAS un signal-only) :**
- **Ligne de navigation** ajoutée (après `PORTAIL_BANCABLES_4BIG.md`) pointant vers une ancre
intra-doc `#directives-michel-input-specs-datés` (ancre non validée par `check_docs` qui
strippe `#…` l.33 → sûr).
- **Nouvelle section `## Directives Michel (input-specs datés)`** (avant `## Git`) : cadre la
nature snapshot (`spec = authority`, comme `daily_reports`), rappelle l'exclusion de
`guard_constraints` (gouvernance citant URLs/chemins interdits), puis **liste les 8
directives en 2 groupes honnêtes** :
- **Pilotent un module byte-gaté** (audité *directive→implémentation*) : FINANCEMENT →
`crm/financement_bancaire` · MOBILE_STORES → `mobile/app_config` · PIE → `pie/manifest`.
- **Contexte/inventaire/workflow/gouvernance** (pas de module 1:1, runtime/VPS hors
périmètre #8) : WORKFLOW_FAISABILITE_V10 · ARCHIVES_DEBLOCAGE · RENDUS_EXISTANTS ·
PLANPOINT_STYLE · UNBLOCK_NOW.
**Discipline anti-drift respectée.** **Aucun compte agrégé en dur** (« 8 directives »/« 4 »)
introduit → pas de scalaire qui dérive en silence (#6) ; l'énumération EST la liste. Tous les
liens ciblent des fichiers **existants** (`check_docs` HARD vérifie l'existence). Groupement
**vérifié sémantiquement** (V10 n'a PAS de module 1:1 → classé contexte, pas faux-lié ;
`faisabilite/AGENT.md` référence en réalité PIE, pas V10). Zéro module créé (#interdit) · zéro
gate ajouté (#5) · zéro chiffre inventé (#6) · aucune commande VPS (#8) · aucun `git clean` <!-- ci-allow : mention de l'interdit absolu, jamais exécuté -->.
**Vérification :** `run_ci.sh --static` → **7/7 PASS** (dont `check-docs` liens OK +
`check-readme-claims` vert : aucune nouvelle assertion chiffrée). Baseline pré-édition
`run_ci.sh` complet = 32 PASS ; édition README-seule → suites de module inchangées.
**Résultat : 1 édition doc RÉELLE** (README indexe enfin la classe `DIRECTIVE_*.md`), classe
[[directive-vs-implementation]] rendue **navigable depuis le point d'entrée**. Gate vert.
## Session 20260803_190730 · Audit directive-vs-SPEC (couple NEUF) → édition doc RÉELLE + SIGNAL Michel
**Classe [[directive-vs-implementation]] étendue à un couple encore non audité :
`DIRECTIVE_PLANPOINT_STYLE_20260803.md` (2026-08-03) ↔ `specs/CHOISIR_MON_UNITE_SPEC.md`
(DÉCISIONS FINALES 2026-07-28).** Jusqu'ici seuls FINANCEMENT / MOBILE / PIE / V10 étaient
triangulés ; PLANPOINT était classé « contexte/UI, pas de module byte-gaté » et n'avait donc
jamais été confronté à sa **spec** de destination (qui, elle, existe : `specs/CHOISIR_MON_UNITE_SPEC.md`).
**Sondes préalables — 3 classes réelles, toutes CLEAN (0 à corriger) :**
1. **docstring/README-vs-CODE** sur les 22 générateurs (Explore dédié) → 0 sortie fantôme,
0 sortie non documentée, 0 input/fallback fantôme. Classe [[docstring-vs-code-drift]]
(récurrente) confirmée close après la dernière évolution de code.
2. **Réconciliation des comptes README** — « 24 suites gated » ↔ « 32 PASS » CI : réconcilié
par le bloc `coverage` de `regression_plan.json` (`suites_count=24` + `gated_in_ci=25` incluant
le self-module `qa/regression`) ; « 24/24 modules » = `quality_report` (24 modules). Aucun
scalaire présent-tense dérivant. Tous les gates `ci/*.sh` ont bien une ligne dans `ci/README.md`
(0 gate sans doc).
3. **daily_reports/2026-08-03.md** = série temporelle horodatée cohérente (blocs 22/22→24/24 =
snapshots successifs, pas dérive) — pas de chiffre présent-tense périmé.
**Le gap RÉEL trouvé (et corrigé — édition doc, PAS signal-only).** La spec
`CHOISIR_MON_UNITE_SPEC.md` proclame « **DÉCISIONS FINALES MICHEL (2026-07-28)** · TOUTES
TRANCHÉES », mais la directive **PlanPoint (2026-08-03, postérieure de 6 jours)** en **contredit
deux** — et **rien dans le repo ne reliait ces deux inputs datés de Michel** (`grep` : aucun
signal antérieur dans activity_log / daily_reports ; la spec n'est référencée que depuis
`03_agents/frontend_console/AGENT.md`, en simple lien). La spec sur-affirmait donc la finalité.
Les **2 contradictions dures** (+ 1 précision) :
| Point | Spec 2026-07-28 | Directive 2026-08-03 (postérieure) |
|---|---|---|
| **#4 Signature** | **DocuSign** (SaaS externe) | **OTO Sign™** (§7 · signature électronique native OTO) |
| **#5 Comparateur** | **NON** (une unité à la fois) | **« Comparateur · max 3 unités »** (§6 · réintroduit) |
| Acompte (*précision*) | **20 %** | **20 % résident / 30 % étranger** + Promesa Ley 126-02 (§7) |
**Édition (spec, doc RÉELLE) :** encart `> ⚠️ Arbitrage rouvert` inséré en tête du bloc
DÉCISIONS FINALES. Il **surface** la divergence, **cite** la directive postérieure (lien relatif
`../../DIRECTIVE_PLANPOINT_STYLE_20260803.md`, existence vérifiée par `check_docs`), et énonce les
2 contradictions + la précision acompte — **sans modifier aucune des 5 décisions** (spec = autorité
jusqu'à arbitrage Michel ; directive = snapshot daté). La spec devient **honnête** : elle ne
prétend plus « tout tranché » en silence alors qu'un input Michel plus récent rouvre 2 points.
**Discipline anti-drift.** La spec n'est **PAS byte-gatée** (absente d'`audit_4big` registry [24
modules], d'`check_artifacts`, d'aucun `ci/*.sh`) → édition **gate-neutre**, aucun consommateur aval.
Aucun chiffre inventé (#6) : tout provient des 2 fichiers Michel. « DocuSign »/« OTO Sign™ » ne sont
pas des termes interdits (guard_constraints mord GitHub/Stripe/EspoCRM/HubSpot). Zéro décision flippée
unilatéralement (respect #6 + [[directive-vs-implementation]]).
**SIGNAL Michel — 2 arbitrages produit à trancher sur `/choisir-mon-unite` :**
- **(a) Signature** : conserver **DocuSign** (spec 07-28) ou basculer sur **OTO Sign™** (directive
08-03 · produit natif, cohérent avec la préférence « natif d'abord ») ?
- **(b) Comparateur** : maintenir le **NON** (07-28) ou activer le **comparateur max 3 unités**
(08-03) ?
Selon l'arbitrage, la spec DÉCISIONS FINALES sera re-tranchée (et l'acompte étranger 30 % + Ley
126-02 intégrés). Analogue au SIGNAL V10 (condition #4 Financement) : couche directive postérieure
supersédant une décision figée = **décision produit Michel, pas édition worker**.
**Vérification :** `run_ci.sh --static` → **7/7 PASS** (check-docs lien OK · guard vert) ; puis
`run_ci.sh` complet → **32 PASS · 0 FAIL · 0 SKIP**. Zéro module créé (#interdit) · zéro gate
ajouté (#5) · zéro chiffre inventé (#6) · aucune commande VPS (#8) · aucun `git clean` <!-- ci-allow : mention de l'interdit absolu, jamais exécuté -->.
**Résultat : 1 édition doc RÉELLE** (spec `choisir-mon-unite` rendue honnête sur la divergence
directive→spec) + **1 SIGNAL** à 2 arbitrages. Couple `DIRECTIVE_PLANPOINT_STYLE ↔ CHOISIR_MON_UNITE_SPEC`
désormais audité et **navigable/traçable** entre les deux inputs Michel.
---
## Session 20260803_193734 · Directive-index honnêteté + fermeture audit 8 directives
**Contexte** — 8 directives `DIRECTIVE_*.md` à la racine ; 5 couples déjà audités les sessions précédentes (FINANCEMENT, MOBILE, V10, PIE, PLANPOINT↔spec). Restaient 3 non-couvertes : ARCHIVES_DEBLOCAGE, RENDUS_EXISTANTS, UNBLOCK_NOW.
**Sonde 1 · les 3 directives restantes = runtime/VPS hors périmètre.** ARCHIVES (inventaire IFC/plans `/opt/oto/data_room`), RENDUS (inventaire rendus `/var/www/html/static/projets`), UNBLOCK (autorisations 24/7 + snapshot ERPNext prod). Aucune ne pilote un module byte-gaté in-repo → classement README bucket-2 (« pas de module byte-gaté 1:1 · exécution runtime hors périmètre #8 ») **correct**. Toutes indexées au README (8/8). CLEAN.
**Sonde 2 · noms de marque directives vs PIE.** RENDUS liste 7 projets par **marque** (P01 Coralis, P02 Coral del Sur, P03 Nakua, P05 Las Colinas, P07 Aqua Terra, P08 Fasano Espirilla, P09 Résidence Gazcue) — **match exact 7/7** avec `pie_manifest.json` champ `marque`. Couche marque ≠ code-name §Projets (P01 Structure / P09 1069 Crisfer), les deux séparément gatés (cf. mémoire `project-brand-vs-codename-not-drift`). NON-dérive. CLEAN.
**ÉDITION DOC RÉELLE · README §Directives, ligne PLANPOINT_STYLE.** La session précédente (a9e9ee9) a ajouté à `specs/CHOISIR_MON_UNITE_SPEC.md` un encart *Arbitrage rouvert* pointant **vers** la directive — mais l'index README décrivait PLANPOINT comme simple « exigence UI » **sans jamais nommer la spec qu'elle pilote**. Asymétrie : le lien spec→directive existait, directive→spec manquait à l'index. Enrichi la ligne 85 avec la flèche `→ specs/CHOISIR_MON_UNITE_SPEC`, la mention des **2 arbitrages rouverts** (signature DocuSign↔OTO Sign™ · comparateur) non tranchés (spec = autorité), et la raison du classement bucket-2 (spec non byte-gatée). Le couple audité directive→spec est désormais **découvrable depuis l'index**, symétrique au back-ref de la spec.
**Gate-safety** — README dans le périmètre de `check_readme_claims` (chiffres) + `check_docs` (liens). Nouveau lien `05_deliverables_mvp/specs/CHOISIR_MON_UNITE_SPEC.md` résout ✓. « 2 arbitrages » = prose fidèle à l'encart de la spec (Décision 4 + Décision 5), pas un compteur recalculé. `check_docs`, `check_readme_claims`, `guard_constraints` verts.
**Résultat** — 1 édition doc réelle (README honnête sur le couple PLANPOINT→spec) · 3 directives restantes fermées CLEAN (runtime hors périmètre, marques 7/7 conformes PIE) · **audit des 8 directives Michel désormais complet** · `run_ci.sh` **32 PASS / 0 FAIL**. Aucune décision produit flippée (spec = autorité).
---
## Session 20260803_200738 · Re-vérification indépendante couple PLANPOINT↔spec (2 contradictions confirmées RÉELLES) + rattrapage canal `daily_reports` (3 sessions de retard) · 0 édition prod
**Contexte + choix de tâche.** `./run_ci.sh` au démarrage : **32 PASS · 0 FAIL · 0 SKIP**, arbre propre. Le cycle « audit des 8 directives Michel » vient de se fermer (`88bd025`). Plutôt que d'ouvrir un 9e audit redondant, j'ai (1) **re-vérifié de façon indépendante** la dernière édition substantielle (l'encart *Arbitrage* inséré dans `specs/CHOISIR_MON_UNITE_SPEC.md` par `a9e9ee9`) — c'est le point le plus susceptible d'abriter un défaut résiduel — puis (2) **rattrapé le canal stakeholder `daily_reports`** qui avait pris 3 sessions de retard sur l'`activity_log` (classe [[two-logging-channels]], déjà corrigée en `063711`).
**Sonde 1 · les 2 contradictions de l'encart sont-elles réelles ? → OUI, confirmées.** Grep croisé directive ↔ spec :
- **Décision 4 · Signature** — spec = **DocuSign** (SaaS externe : l. 58 « Décision 4 : DocuSign intégré » · 89 · section `DocuSign integration` l. 112) ↔ directive `PLANPOINT_STYLE` §7 l. 53 = **« Signature électronique OTO Sign™ »** (produit natif). Contradiction RÉELLE.
- **Décision 5 · Comparateur** — spec = **NON** (l. 59 « Décision 5 : NON » · 90 · 119 « Pas de comparateur ») ↔ directive §6 l. 48 = **« Comparateur · ajouter à liste comparative (max 3 unités) »** + l. 86 « Comparateur 3 unités ». Contradiction RÉELLE.
→ **Aucun faux-positif inséré dans la spec.** L'édition `a9e9ee9` est **factuellement saine**, **0 correction** requise.
**Sonde 1b · nouvel angle sur le SIGNAL Michel (renforcement, non tranché).** La Décision 4 n'est **pas neutre vis-à-vis du mandat** : `CLAUDE.md` #1 impose « ERPNext natif = priorité absolue **avant tout outil externe** ». **OTO Sign™** (directive · natif) est donc **plus conforme** au mandat que **DocuSign** (spec · SaaS externe). La directive est aussi la plus **récente** (08-03 vs spec 07-28) → récence **et** conformité #1 pointent toutes deux vers OTO Sign™. **Je ne flippe pas** (spec = autorité du module tant que Michel n'a pas tranché · [[directive-vs-implementation]] · #5) — mais j'enrichis le SIGNAL de cet angle mandat que les sessions précédentes n'avaient pas explicité.
**Réalisé · rattrapage `daily_reports/2026-08-03.md`.** Un addendum concis couvrant les sessions `180728` (PIE, SIGNAL confotur≠Promesa/Fideicomiso/HOA), `190730`+`193734` (couple PLANPOINT↔spec, 2 éditions doc, fermeture 8 directives) et `200738` (cette re-vérif + angle mandat). Chiffres **100 % re-dérivés d'artefacts committés** (aucun saisi à la main) :
- Qualité **24/24** ← `quality_report.json.coverage` (`registry=ci=24` · `missing_*=[]`)
- Régression **624/624** · 24 suites ← `regression_run.json.totals`
- Recette **15/15** ← `acceptance_matrix.json.matrix`
- Gate **32 PASS** ← `run_ci.sh`
**Sûreté du changement.** `daily_reports/` n'est ni un artefact `out/` byte-gaté ni dans le périmètre `audit_4big`/`check_artifacts` — édition sûre, gate-neutre (même posture que `063711`). Prose de l'addendum fidèle aux artefacts et à l'encart spec (pas de compteur inventé).
**Vérifications.** `./run_ci.sh` → **32 PASS · 0 FAIL · 0 SKIP** (inchangé — aucun fichier de production touché). `git status` : 2 fichiers doc modifiés (`daily_reports/2026-08-03.md` + ce log). Aucune commande VPS (#8). Aucune décision produit flippée (#6, spec = autorité).
**Résultat : 0 édition prod · 1 défaut potentiel écarté** (encart spec sain, 2 contradictions réelles) · **1 SIGNAL Michel enrichi** (OTO Sign™ natif = plus conforme #1 que DocuSign) · **canal `daily_reports` remis à niveau** (3 sessions rattrapées, tout sourcé).
+205
View File
@@ -0,0 +1,205 @@
# Activity Log · 2026-08-04 · Claude Code DTP Worker
## Session 221055 · Passe de vérification d'intégrité inter-artefacts — 5 surfaces (dont 3 NEUVES au compte courant 24/32) → CLEAN 0 édition prod · ouverture canal daily_reports 08-04
**Contexte + choix de tâche.** `./run_ci.sh` au démarrage : **32 PASS · 0 FAIL · 0 SKIP**,
arbre `git status` propre. Roadmap `ROADMAP_8_WEEKS_OR_LESS.md` : les 8 sprints ont leur
module livré, gaté et vert (couverture CI = 32 jobs). Le canal `daily_reports/2026-08-03.md`
est courant (clos à l'addendum `180728→200738`). Les 8 directives Michel sont closes
(commit `88bd025`). Les surfaces de dérive usuelles (docstring-vs-CODE, comptes gatés,
inventaire-vs-FS, constantes de marque #4, noms d'entités/projets, directive↔spec) sont
**saturées** — les re-scanner serait redondant (#5). J'ai donc ouvert une **passe
d'intégrité inter-artefacts** ciblant en priorité les surfaces qui ont pu **dériver avec le
passage récent 22→24 modules** (`100714`, la seule dérive réelle des dernières ~48 h), et
des surfaces de **correctness numérique** jamais explicitement caractérisées.
**5 surfaces auditées — toutes CLEAN. 0 fichier de production/gate/doc modifié.**
1. **Docstring-vs-CODE / comptes de sortie (agent Explore, ~25 générateurs).** Chaque
`*_gen.py` : docstring ↔ README ↔ fichiers réellement écrits (`_write_json`/`json.dump`
dans `cmd_build`). **0 mismatch présent-tense** — chaque module écrit exactement les
fichiers annoncés (seo 4, financement 7, mobile 5, pie 2, demo 3 dont `run_sheet.md`,
etc.). Classe [[docstring-vs-code-drift]] (récurrente) re-confirmée CLEAN à date.
2. **Correctness numérique `commissions` (NEUF).** Le barème garde délibérément
`taux_pct: null` (anti-invention #6 : aucun taux dans CLAUDE.md), et `commlib/finance.py`
calcule `commission = base × taux` = `None` tant qu'un opérande manque — **jamais
0-inventé**. Donc **aucune réconciliation numérique possible ne peut dériver** (pas de
somme/produit matérialisé). Surface caractérisée : *null-by-design, non-candidate à un
audit de réconciliation*.
3. **Réconciliation de compte inter-artefacts au point courant (NEUF au 24).** Tous
concordants : `quality_report.json` verdict **PASS · 24 modules**, bloc `coverage`
**`ci_modules_count == registry_modules_count == 24`** (`ok:true`, `missing_*`/`not_in_gate`
vides, self-exclu `qa/audit_4big`) · `regression_run.json.totals` = **24 suites · 624 ran /
624 passed · 0/0/0** · `acceptance_matrix.json` = **15/15 rows `status=in_repo`**, verdict
`true` · `gate.needs` = **32 jobs**. La table de clôture `daily_reports/2026-08-03.md`
(addendum `100714`) porte déjà ce 24/624/32 réconcilié — **pas de dérive résiduelle**.
Classe qui avait eu une dérive RÉELLE (22→24) → re-vérifiée close.
4. **Résolution `ci_job` recette → `gate.needs` au point courant (NEUF au 24/32).** Les
**36** `matrix[].modules[].ci_job` de la matrice de recette **résolvent tous** vers un job
réel de `gate.needs`**0 orphelin**. Les **2** modules ajoutés au passage 22→24
(`crm/financement_bancaire`, `pie/manifest`) sont **bien présents** dans la matrice (grep
`financement`/`pie`/`manifest` = hit). Les 8 jobs de `gate.needs` **jamais** cités par la
recette sont exactement les **contrôles d'infrastructure CI** (`check-artifacts`,
`check-docs`, `check-readme-claims`, `check-regression`, `check-ci-integrity`,
`constraints-guard`, `validate-json`) + `qa-acceptance-tests` (auto-exclusion, comme le
`self_module_excluded` d'audit_4big) — **absence par design**, pas un trou de couverture.
Prolonge la sonde `000623` (qui l'avait faite à 22/30) au **compte courant 24/32**.
5. **Couverture des jobs par `ci/README.md` (NEUF).** La table §1 est — **par design**
une table des **7 gates statiques `ci/*.sh`** (catégories d'infrastructure), *pas* une
énumération des 24 suites de test par module. Le README l'assume explicitement (§1
« Rejouer le gate », l.56 : la liste des suites est « **dérivée de `gate.needs` et imprimée
à l'exécution** ») et `check_ci_integrity.sh` INV-A **gate dynamiquement** que
`gate.needs == { tous les jobs non-manuels }`. Les quelques mentions de suites de module
dans le §1/§2 (`seo-tests`, `fiscal-ecf-tests`, `rbac-tests`…) sont **incidentes** (prose
d'exemple), pas une liste censée être exhaustive → **pas de gap**. Surface caractérisée
pour éviter tout futur faux-positif « ci/README ne liste pas les 24 jobs ».
**Aucune tâche fonctionnelle in-repo restante.** Balayage des 8 `DIRECTIVE_*.md` + prose
des livrables : tous les marqueurs « à créer / TODO » restants pointent des **DocTypes
ERPNext côté VPS** (hors périmètre worker #8) ; les seuls livrables-fichiers versionnés que
la directive Mobile classait in-scope (`mobile-build.yml`) sont **déjà faits** (`.gitea/
workflows/` = `ci.yml` + `mobile-build.yml`). Le module Publiciste (GAP_ANALYSIS l.50
« New/à créer ») **existe** (`publiciste.py`, importé par `commissions_gen`). La roadmap est
**intégralement livrée et gatée** — inventer une modification fonctionnelle sur un système
complet violerait #5 (doublon) / #6 (invention). La valeur honnête restante est la
**vérification d'intégrité** + la **currency du canal stakeholder**.
**Reporting canal 2 (contrat « Rapports quotidiens »).** Ouverture de
`daily_reports/2026-08-04.md` (nouveau jour ; le `2026-08-03` clos à `200738`). Snapshot
launch-readiness **100 % sourcé** par recompute `python3` indépendant contre les artefacts
commités (anti-invention #6) — mêmes 4 dimensions que la clôture 08-03, inchangées car
audit pur : 24/24 qualité · 24 suites/624 régression · 15/15 recette · 32 PASS local.
**Vérifications.** `./run_ci.sh`**32 PASS · 0 FAIL · 0 SKIP** (inchangé — audit +
reporting seuls, aucune logique de prod/gate touchée). Recompute indépendant concordant sur
les 4 dimensions. Arbre propre avant édition. **Zéro nouveau module · zéro gate ajouté
(#5) · zéro chiffre inventé (#6).** Aucune commande touchant au VPS (#8) · aucun
`git clean` <!-- ci-allow : mention de l'interdit absolu, jamais exécuté -->.
---
## Session 224101 · Sonde de dérive NON gatée (4 dimensions neuves) → CLEAN + caractérisation d'un faux-positif contact d'escalade · 0 édition prod
**Contexte + choix de tâche.** `./run_ci.sh` au démarrage : **32 PASS · 0 FAIL · 0 SKIP**,
arbre `git status` propre. Les 8 sprints roadmap sont livrés/gatés/verts ; le reste
(déploiement prod, builds EAS, dépôts ONAPI) est hors périmètre worker (#8). La session
précédente `221055` (~90 min avant) avait déjà passé une audit inter-artefacts complète
(5 surfaces, CLEAN) et ouvert le canal `daily_reports/2026-08-04`. Re-jouer les surfaces
usuelles serait redondant (#5). J'ai donc lancé un agent Explore sur **4 dimensions de
dérive non-gatée JAMAIS explicitement caractérisées** — orthogonales aux classes saturées
(docstring-vs-code, comptes gatés, constantes #4, noms d'entités/projets, directive↔spec,
ci_job→gate.needs, bijection module↔artefact).
**4 dimensions sondées — toutes CLEAN. 0 fichier de production/gate/doc modifié.**
1. **Cross-refs entre fiches AGENT.md** : chaque agent cité (bim, crm, publiciste, qa,
rendu, seo, faisabilité…) existe bien sous `03_agents/`**0 nom pendant**.
2. **Dates/session-ids dans READMEs & specs** : aucune date dans le **futur** de
2026-08-04 ; specs (`CHOISIR_MON_UNITE`) citent 07-28/08-03 (passé). → CLEAN.
3. **Contacts d'escalade** (CLAUDE.md · roadmap L90-91 · fiches · assets) :
**téléphone `+18296296385` = 15 surfaces byte-identiques** (13 fiches + roadmap L90
+ `AGENTS_EXISTING_ASSETS` L106 + `GAP_ANALYSIS`). **Email `mroy@worldactivitiesrd.com`
= 1 occurrence** (roadmap L91, canal décision-archi). L'agent l'avait signalé comme
« référence pendante » (occurrence unique). **Faux-positif confirmé** : occurrence
unique ≠ contradiction ; l'email est à sa place (doc qui porte l'escalade) et le domaine
`worldactivitiesrd.com` correspond à l'entité « World Activities RD » (CLAUDE.md §Entités,
WA SRL). → **NON drift, aucune correction.** Caractérisé en mémoire
(`escalation-contact-not-drift`) pour pré-empter le re-flag (contact = prose non gatée).
4. **Specs ↔ répertoires deliverables** : chaque module cité en spec (faisabilite/bancable,
legal/confotur, publiciste, mobile/app_config, pie/manifest, qa/acceptance,
crm/workflow_vente, seo, rbac…) a son répertoire sous `05_deliverables_mvp/`. → CLEAN.
**Décision anti-invention (#6).** Aucune édition de production n'est justifiée : la seule
« anomalie » remontée était un faux-positif. Fabriquer un correctif (retirer/propager
l'email) violerait #6 et introduirait une vraie régression. Session **signal-only** +
1 note mémoire anti-redondance.
**Vérifications.** `./run_ci.sh`**32 PASS · 0 FAIL · 0 SKIP** (inchangé — sonde read-only,
aucune logique prod/gate touchée). **0 nouveau module · 0 gate ajouté (#5) · 0 chiffre
inventé (#6).** Aucune commande VPS (#8) · aucun `git clean` <!-- ci-allow : mention de l'interdit, jamais exécuté -->.
---
## Session 231101 · Attestation indépendante des chiffres stakeholder (recompute vs artefacts) → CLEAN · 0 édition prod
**Contexte + choix de tâche.** `./run_ci.sh` au démarrage : **32 PASS · 0 FAIL · 0 SKIP**,
arbre `git status` propre. Roadmap intégralement livrée/gatée (8 sprints) ; le reste est
hors périmètre worker (#8). Deux sessions du jour (`221055`, `224101`) avaient déjà passé
l'audit inter-artefacts + une sonde de dérive non-gatée → CLEAN. Re-sonder serait redondant
(#5). J'ai donc changé d'angle : **attester** que les 4 chiffres launch-readiness publiés
au canal stakeholder ne sont pas de l'artefact-echo mais **rechargent à l'identique depuis
les artefacts commités**, via recompute `python3` indépendant (charge le JSON, recompte —
ne copie pas la table · #6).
**Vérification menée — 4 dimensions rechargées, 0 écart.**
1. **Qualité 4Big** (`qa/audit_4big/out/quality_report.json`) : 24 modules, **tous score
== 100**, `coverage.ok=true`, `ci_modules_count == registry_modules_count == 24`,
0 manquant CI/registry. → concorde avec « 24/24 à 100/100 ».
2. **Régression** (`qa/regression/out/regression_run.json.totals`) : **24 suites · 624 ran
· 624 passed · 0 fail/err/skip**, verdict `PASS`. → concorde avec « 24 suites/624 ».
3. **Recette** (`qa/acceptance/out/acceptance_matrix.json`) : matrice = **15 lignes toutes
`status=in_repo`**, verdict `true`. La chaîne `ci_job` apparaît **36×** — nichée dans
`matrix[].modules[]` (PAS au niveau ligne : un premier parse naïf au niveau ligne rend
0, faux-négatif écarté). → concorde avec « 15/15 · 36 ci_job → 0 orphelin »
(l'absence d'orphelin restant prouvée par `check_ci_integrity`).
4. **Miroir local** : `./run_ci.sh`**32 PASS · 0 FAIL · 0 SKIP**.
**Confirmation de couverture (pas d'édition, attestation).** L'artefact d'EXÉCUTION
`regression_run.json` est **exclu** de `check_artifacts` (qui ne rejoue que `build`) mais
**protégé** par son gate dédié `ci/check_regression.sh` — il régénère `run` frais vers un
tmp et exige l'identité byte-for-byte, verdict PASS. Les totaux 624 ne peuvent donc pas se
périmer en silence si une suite grandit/rétrécit. Surface confirmée gatée, pas un trou.
**Décision anti-invention (#6).** Aucune anomalie → aucune édition de production justifiée.
Session **attestation-only** : 2e addendum au `daily_reports/2026-08-04` (recompute
stakeholder) + cette entrée. Pas de note mémoire neuve (le fait — chiffres stakeholder
sourcés — est déjà couvert par `two-logging-channels` / `doc-numeric-claims-gate`).
**Vérifications.** `./run_ci.sh`**32 PASS · 0 FAIL · 0 SKIP** (inchangé — recompute
read-only, aucune logique prod/gate touchée). **0 nouveau module · 0 gate ajouté (#5) ·
0 chiffre inventé (#6).** Aucune commande VPS (#8) · aucun `git clean` <!-- ci-allow : mention de l'interdit, jamais exécuté -->.
---
## Session 234104 · Durcissement `mobile-build.yml` — gating d'activation robuste (secrets-context hors `if:` job) → 1 édition CI in-scope
**Contexte + choix de tâche.** `./run_ci.sh` au démarrage : **32 PASS · 0 FAIL · 0 SKIP**,
arbre propre. Roadmap intégralement livrée/gatée ; les 3 sessions du jour (`221055`,
`224101`, `231101`) avaient déjà passé audit inter-artefacts + sonde de dérive + attestation
stakeholder → CLEAN. Re-sonder une 4e surface d'audit serait redondant (#5). J'ai donc
changé de registre : au lieu d'un audit read-only de plus, **vérifier la correctness d'un
artefact CI in-scope à impact futur réel** — le workflow `mobile-build.yml` que le canal
stakeholder désigne comme « prochaine tâche » (s'active quand Michel fournit `EAS_TOKEN`).
Un agent Explore a d'abord confirmé **0 bug de logique** dans les générateurs Python (classe
correctness, pas comptes) — cohérent avec la saturation. Le défaut réel était dans le YAML CI.
**Défaut trouvé (latent, silencieux).** Les deux jobs `build-ios` / `build-android`
gataient sur `if: ${{ secrets.EAS_TOKEN != '' && … }}` **au niveau job**. Or le `secrets`
context n'est **pas** dans la table d'availability de `jobs.<id>.if` de GitHub Actions
(seuls `github`/`needs`/`vars`/`inputs`/`matrix` + status functions y sont) ; le moteur
`act` de Gitea peut l'exposer ou non → comportement **ambigu selon la plateforme**. Risque
concret : le jour où Michel ajoute un `EAS_TOKEN` valide, les builds pourraient rester
**SKIP en silence** (`secrets.EAS_TOKEN` évalué vide en contexte `if:` → condition toujours
fausse). Exactement le « marche dans ma tête, casse le jour où on en a besoin ».
**Correctif (pattern canonique preflight-output, portable GitHub ET Gitea/act).** Le
secret est désormais lu **dans un step** de `preflight` (où `secrets` est toujours
disponible), converti en `outputs.has_token` / `has_repo` (booléens via `$GITHUB_OUTPUT`),
et les jobs de build gatent sur `needs.preflight.outputs.*` — contexte, lui, **disponible
en `if:` partout**. **Contrat observable inchangé** : `preflight` reste vert et rapporte
l'état des secrets ; les builds restent SKIP tant que le token est absent (prose
`ci/README.md` §Second workflow toujours exacte, pas de dérive doc). Le durcissement porte
seulement sur *comment* le SKIP est décidé.
**Vérifications.** `python3 -c yaml.safe_load`**YAML valide** ; `preflight.outputs`
présents ; `build-ios`/`build-android` gardent `needs: preflight` ; **0** `secrets.` restant
dans un `if:` de job ; 4 écritures `$GITHUB_OUTPUT`. Aucun gate ne linte la structure de
`mobile-build.yml` (`check-ci-integrity` ne verrouille QUE `ci.yml`) → pas de gate cassé.
`./run_ci.sh`**32 PASS · 0 FAIL · 0 SKIP** (inchangé — le workflow mobile est hors
`gate.needs`). **1 fichier CI édité (in-scope, le worker versionne la CONFIG CI/CD) · 0
nouveau module (#5) · 0 chiffre inventé (#6) · aucune commande VPS (#8) · aucun `git clean`** <!-- ci-allow : mention de l'interdit, jamais exécuté -->.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,136 @@
# Registre des décisions ouvertes · OTO Enterprise OS DTP
> **But** — Punch-list unique, sourcée, des **décisions produit qui appartiennent à Michel**
> (arbitrages de périmètre / dépendances runtime hors dépôt), consolidée depuis les journaux
> de session où elles ont été *surfacées* au fil de l'eau. Ces items **ne sont pas des bugs
> worker** : les corriger unilatéralement serait soit une invention de périmètre (#6), soit
> une réécriture d'une source d'autorité. Ce registre **regroupe et source**, il ne tranche pas.
>
> **Portée** — CI dépôt **verte** : tous les jobs de `gate.needs` passent · tous les modules 4Big
> au seuil `100/100`, verdict `PASS`. *(Décomptes exacts — dérivés et gatés — dans le README
> `## État courant` et `qa/audit_4big/out/quality_report.json` ; jamais figés ici, ils dérivent
> quand un gate ou un module est ajouté.)* Aucun de ces items
> ne casse un gate ; ils concernent le **périmètre produit** et l'état runtime **hors dépôt**
> (VPS `153.75.250.214` · `CLAUDE.md` #8), non vérifiable/éditable depuis le worker.
>
> **Mise à jour** — 2026-08-05 · session `20260805_014119`. Chaque ligne cite sa source exacte
> et la session qui l'a surfacée en premier. **Ne pas re-surfacer** ces items en doublon (#5) :
> pointer ici.
---
## Légende statut
- 🟠 **OUVERT** — décision produit en attente de Michel (ou d'un agent hors dépôt).
- 🟢 **VÉRIFIÉ-SANS-SUITE** — soupçon investigué → aucun risque réel ; consigné pour ne pas ré-ouvrir.
---
## D-01 · 🟠 PIE : `contrats` attribué à `legal/confotur` qui ne génère pas ces contrats
**Décision attendue** — Soit (a) construire un vrai module `contrats` (Promesa de compraventa ·
Fideicomiso d'adhésion · règlement HOA), soit (b) rebasculer l'attribution à `"module": null`
comme les autres downstreams sans générateur (brochures/plans/rendus/bim).
**Constat** — Le registre downstream PIE mappe `contrats → legal/confotur` :
- `05_deliverables_mvp/pie/manifest/pie_spec.json:24`
`{ "cle": "contrats", "libelle": "Contrats types (Promesa · Fideicomiso · HOA)", "module": "legal/confotur" }`
- Écho dans l'artefact gaté : `05_deliverables_mvp/pie/manifest/out/pie_manifest.json:165-166`,
`out/MANIFEST.json:24`, et la table `README.md:48`.
Or `legal/confotur` produit **uniquement le DocType Frappe de la demande CONFOTUR** (Ley 158-01),
pas de gabarit Promesa/Fideicomiso/HOA :
- `05_deliverables_mvp/legal/confotur/README.md:8-17` (sorties = `doctype_confotur_application.json`
+ `MANIFEST.json`, rien d'autre).
**Incohérence interne** — le même registre utilise déjà `"module": null` pour tout downstream
sans générateur (`pie_spec.json:22,27,28,29,30`). `contrats` est le seul downstream attribué à
un module qui ne le produit pas. **Arbitrage de périmètre, pas une correction worker.**
**Sources / surface** — décrit dans la mémoire projet `directive-vs-implementation` (« PIE SIGNAL »).
Non édité : cf. convention *SURFACE, don't rewrite*.
---
## D-02 · 🟠 Financement bancaire : condition #4 encore sur `wag_validated_by` (humain), directive V10 la remplace par un audit IA signé
**Décision attendue** — L'agent **OTO Auditeur Finances** (IA autonome, **hors dépôt**) doit
exister/émettre un `audit.decision == APPROVED` signé pour que la condition #4 soit implémentable.
Tant qu'il n'existe pas, le module ne peut pas migrer sans casser le gate de financement.
**Constat** — La directive porte une couche `## PRÉCISION` (2026-08-03) qui **supersede**
explicitement la validation humaine « conseiller WAG » par une décision d'agent IA :
- `DIRECTIVE_FINANCEMENT_BANCAIRE_COMPLET_20260803.md:206-210` et `:275-279`
`condition_4_ok = audit and audit['decision'] == 'APPROVED' and audit['signature_valid']`.
Le module livré implémente encore l'**ancienne** condition humaine :
- `05_deliverables_mvp/crm/financement_bancaire/finlib/gate.py:142-145`
`_cond_validation_wag` teste `dossier.get("wag_validated_by")`.
- `05_deliverables_mvp/crm/financement_bancaire/financement_spec.json:157` (label « Validation
manuelle WAG confirmée par le conseiller référent »).
**Pourquoi worker n'édite pas** — la nouvelle condition dépend d'un producteur d'`audit` signé
qui n'existe pas dans le dépôt ; migrer le gate maintenant le rendrait toujours-faux. **Arbitrage
séquencement produit + dépendance agent hors dépôt.**
**Sources / surface** — mémoire `directive-vs-implementation` (« V10/173726 SIGNAL »).
---
## D-03 · 🟠 Inventaire : `otoia/capabilities/chat.py` absent au chemin, alors que `CLAUDE.md` le liste comme capability canonique
**Décision attendue** — Soit recréer `chat.py`, soit retirer `chat.py` de la liste des
capabilities OTOIA canoniques dans `CLAUDE.md` §Architecture cible.
**Constat**
- `AGENTS_EXISTING_ASSETS.md:102` le déclare présent (« conversation »).
- `CLAUDE.md:28` le liste (« aec.py + knowledge.py + prompt_engine.py + chat.py »).
- Filesystem : `/opt/oto/otoia/capabilities/chat.py` **absent** (ni `.py` ni `.pyc`).
- Auto-incohérence : le footer du même inventaire (L128) **omet déjà** `chat.py`.
**Pourquoi worker n'édite pas** — corriger l'inventaire contredirait la constitution `CLAUDE.md` ;
l'inverse toucherait la constitution. **À trancher par Michel.** (Distinct du `chatbot-lead`
qui, lui, existe — ne pas confondre.)
**Sources / surface****déjà surfacé** session `20260803_133718`
(`05_activity_log/2026-08-03.md:1245-1250`).
---
## D-04 · 🟠 Inventaire : `config/projets_editor.py` « API GET/POST déjà en place » — source absente (claim runtime non réfutable)
**Décision attendue** — Confirmer si l'API `projets_editor` tourne effectivement (déployée
ailleurs sur le VPS) ou si le claim d'inventaire est obsolète.
**Constat**
- `AGENTS_EXISTING_ASSETS.md:127` : `/opt/oto/config/projets_editor.py (API GET/POST déjà en place)`.
- Filesystem : aucun `projets_editor*` sous `/opt/oto` ; le sibling `projets_config.json` existe.
- Le claim porte sur une **API VPS live****non réfutable** par la seule absence de source
in-`/opt/oto` (peut tourner déployée ailleurs, #8). Non touché.
**Sources / surface****déjà surfacé** session `20260803_133718`
(`05_activity_log/2026-08-03.md:1251-1253`).
---
## D-05 · 🟢 Mobile : identifiant bundle `com.otov7.app` face au renommage OTOV7 → « OTO Enterprise OS » — VÉRIFIÉ, aucun risque
**Verdict** — Le renommage produit **ne menace pas** l'App Store #32. L'`app.name` passe bien à
« OTO Enterprise OS », mais tout identifiant de store est **quarantiné `null · a_confirmer`,
jamais fabriqué**, et **découplé** du nom d'app par conception (#6/#8) :
- `05_deliverables_mvp/mobile/app_config/mobile_spec.json` (`bundleIdentifier`/`package` en
`a_confirmer`, « jamais fabriquée #6/#8 »).
- `out/app_config.json``bundleIdentifier: null`, `package: null`.
- Constante historique figée `com.otov7.app` : `DIRECTIVE_MOBILE_STORES_20260803.md:15-16`.
Aucun chemin de code ne peut auto-muter le bundle id. La mise à jour réelle de #32 (si besoin)
est **manuelle, hors dépôt**. Consigné ici pour éviter de ré-ouvrir le soupçon (mémoire
`directive-vs-implementation` « mobile SIGNAL »).
---
## Rappel de discipline
- Ces items sont **surfacés, pas tranchés** : les éditer côté worker violerait #6 (invention) ou
réécrirait une source d'autorité (`CLAUDE.md`, une directive datée).
- **Ne pas re-signaler** en doublon dans les rapports quotidiens (#5) — **pointer ce registre**.
- Rien ici ne bloque la CI dépôt (verte) ; tout relève du **périmètre produit** ou du **runtime
hors dépôt** (VPS · #8).
@@ -1,12 +1,12 @@
# Financement Bancaire · parcours hypothécaire immobilier RD
**Sprint 5 · CRM natif ERPNext · roadmap L52 (workflow vente end-to-end · volet
**Sprint 4 · CRM natif ERPNext · roadmap L52 (workflow vente end-to-end · volet
financement).** Matérialise la
[`DIRECTIVE_FINANCEMENT_BANCAIRE_COMPLET_20260803.md`](../../../DIRECTIVE_FINANCEMENT_BANCAIRE_COMPLET_20260803.md)
(Michel, 2026-08-03) : un module **complet** de financement bancaire pour un vrai
parcours hypothécaire dominicain, à la place du module « trop simpliste »
antérieur. Livrable **Phase 1 (P0 · MVP)** : le contrat de données + le **cœur
métier du gate check** + la structure des 6 sections + la bannière critique.
métier du gate check** + la structure des 7 sections + la bannière critique.
> Ce worker **n'écrit jamais sur le VPS** (contrainte #8). Il produit le contrat
> et les artefacts en-repo ; le backend `/api/hypotheque/*` et le frontend
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
"""Générateur du module Financement Bancaire — parcours hypothécaire RD.
Sprint 5 · CRM natif · roadmap L52 (« workflow vente end-to-end » — volet
Sprint 4 · CRM natif · roadmap L52 (« workflow vente end-to-end » — volet
financement) + DIRECTIVE_FINANCEMENT_BANCAIRE_COMPLET_20260803.md (Michel).
Transforme le contrat `financement_spec.json` en artefacts prêts à consommer
@@ -19,7 +19,8 @@ Le CŒUR métier (calcul d'apport 20/30 + gate check 4 conditions) vit dans
charger le contrat, assembler le bundle, valider (schéma + invariants), écrire.
Anti-invention (#6) : aucune valeur (taux d'apport, documents, autorisations,
noms de rôle) n'est codée en dur — tout vient du contrat + du contrat RBAC. Ce
noms de rôle) n'est codée en dur — tout vient du contrat + du contrat RBAC
(`rbac_50_roles.json`, résolu par `RoleResolver.from_path`, invariant 4). Ce
worker n'écrit JAMAIS sur le VPS (l'application réelle est côté serveur).
Sortie déterministe (tri stable, aucun horodatage) → diffable + re-générable.
@@ -246,3 +246,132 @@ doit balayer **toutes** les surfaces en un seul passage (mémoire
**Hors périmètre (inchangé · #8).** Exécution serveur `eas build`/`eas submit` (attend
`EAS_TOKEN` Michel + `MOBILE_NATIVE_REPO`), déploiement production VPS → agents
Mobile/DevOps.
## Addendum · Session `20260803_100714` · Sprint 8 QA — réconciliation compte modules 22→24 (drift `check-regression` + `check-readme-claims`)
**Drift trouvé à l'ouverture.** `./run_ci.sh` = **30 PASS · 2 FAIL** (`check-regression`,
`check-readme-claims`). Deux modules ajoutés par les sessions auto-exec précédentes —
`crm/financement_bancaire` (directive Financement Bancaire) et `pie/manifest` (Annexe 12
V10.1 · PIE) — avaient été committés **sans régénérer les artefacts/prose aval**. La
matrice de régression `run` était restée à **23 suites / 604 tests** (plan déjà à 24 →
divergence plan↔run) et toute la prose data-derived affichait encore **23/23**.
**Correctif (tout re-dérivé d'artefacts commités).** `regression_gen.py build+run`
`regression_run.json` re-généré (byte-gaté). `audit_4big_gen.py build` re-généré
**après** l'édition des README (l'audit score le contenu doc des modules). Réalignement
prose : README racine (24/24 · 24 suites), `qa/audit_4big/README.md` (24/24), 3 fiches
`03_agents/{qa,erpnext_backend,devops}/AGENT.md`, table run-book `devops/deploy_runbook/README.md`
(phases 4 + 6 : ajout `crm/financement_bancaire` et `pie/manifest`).
**Signal Michel — IP backup.** `check-readme-claims` flaggait aussi
`DIRECTIVE_WORKFLOW_FAISABILITE_V10…md:104` citant `153.75.232.237` (« VPS backup »),
≠ IP canonique `153.75.250.214` de CLAUDE.md §VPS. Doc **root-owned non éditable** par le
worker. Gate étendu **étroitement** (précédent `guard-tracked-files-exclusion`) : une IPv4
explicitement étiquetée `backup <ip>` = serveur de secours légitime, tolérée ; toute IP
primaire/non-étiquetée migrée reste bloquée (mutation-testée). **Action demandée à Michel :
porter l'IP du serveur backup dans `CLAUDE.md §VPS`** pour rétablir la couverture SSOT.
## État launch-readiness · addendum `100714` (100 % sourcé)
| Dimension | Valeur | Verdict | Source commitée |
|---|---|---|---|
| Qualité 4Big | **24/24 modules à 100/100** (seuil 95 · #5) | `PASS` | [`qa/audit_4big/out/quality_report.json`](../qa/audit_4big/out/quality_report.json) |
| Régression (run) | **24 suites · 624 ran · 624 passés · 0/0/0** | `PASS` | [`qa/regression/out/regression_run.json`](../qa/regression/out/regression_run.json) |
| Recette roadmap | **15/15 promesses** `status=in_repo` | `true` | [`qa/acceptance/out/acceptance_matrix.json`](../qa/acceptance/out/acceptance_matrix.json) |
| Miroir local | `run_ci.sh`**32 PASS · 0 FAIL · 0 SKIP** | vert | [`run_ci.sh`](../../run_ci.sh) |
## Addendum · Session `20260803_160723` · Sprint 8 QA — triple-sonde drift réel (0 dérive, 0 édition)
**Session de vérification pure** sur **3** classes de dérive **réelle** (celles à l'origine
de vrais fix passés), toutes confirmées closes après la dernière évolution de code :
1. **docstring/README-vs-CODE** sur les 22 générateurs (claims Entrée/Sortie/Fallback vs
`_write_json`/`_ARTIFACTS` réels) → **0 sortie fantôme, 0 sortie non documentée**.
2. **Bijection module↔artefacts** (classe du drift 22→24 de `100714`) → constatée
**déjà auto-gatée** par le bloc `coverage` de `quality_report.json`
(`ci_modules_count=registry_modules_count=24` · `missing_*=[]` · `not_in_gate=[]`) —
filet anti-récidive **aucun gate à ajouter (#5)**.
3. **Count-drift présent-tense UNGATED** post-réconciliation 22→24 / 569→624 → 3 résidus,
tous classés **KEEP** (compte de tests local `bancable` 22/22 = `Ran 22 tests`·OK ;
`ci/README.md:197` pédagogique-hypothétique ; `ci/README.md:729` historique-repro).
**Aucun fichier de production/doc modifié.** `./run_ci.sh`**32 PASS · 0 FAIL · 0 SKIP**
inchangé. Launch-readiness **inchangé et intégralement sourcé** : 24/24 qualité · 624/624
régression · 15/15 recette · 32 gate.needs verts. Hors périmètre (inchangé · #8) :
déploiement production VPS (`eas build/submit`, bench migrate, nginx/systemd, voix Amélie).
## Addendum · Session `20260803_173726` · Audit directive-vs-implémentation (V10) — SIGNAL Michel gate condition #4
**Session de vérification pure** (0 édition prod). 3e couple `directive-vs-implementation` :
`DIRECTIVE_WORKFLOW_FAISABILITE_V10` (annexe méta-constitution EAOF). V10 est un snapshot de
formalisation (Bureau Virtuel 12 directions · Stage Gates G0-G8 · Annexes 1-11) **sans module
in-repo** → rien à gater. Triangulation **6 banques** V10 ↔ `DIRECTIVE_FINANCEMENT`
`financement_spec.json` = **CLEAN** (ensemble identique, noms complets alignés).
**SIGNAL Michel — arbitrage produit sur la 4e condition du gate Financement Bancaire.**
La directive `FINANCEMENT_BANCAIRE_COMPLET` contient **3 couches internes** dont la plus
tardive **supersède** la 4e condition du gate :
| Couche directive | 4e condition | Source |
|---|---|---|
| Corps + AMENDEMENT | « conseiller WAG a validé » (`wag_validated_by NOT NULL`) | l. 115, 161 |
| **PRÉCISION (la + tardive)** | « **N'EST PAS un humain WAG** » → OTO Auditeur Finances IA : `audit['decision']=='APPROVED' and audit['signature_valid']` | l. 206-300 |
Le module `crm/financement_bancaire` implémente **la couche supersédée** (`gate.py::_cond_validation_wag`
= `wag_validated_by` ; message « Validation WAG manquante ») — verrouillée sur code + tests +
README + artefacts byte-gatés (`gate_spec.json`/`gate_status_example.json`, clé `validation_wag`).
**Zéro wiring auditeur** dans le module (la capability OTOIA `auditeur_finances` est un agent
séparé, inexistant in-repo).
**Non édité, par design** (`directive-vs-implementation` : spec=autorité, directive=snapshot,
SURFACE≠fix). Migrer la condition #4 casserait le byte-repro de 2 artefacts gatés, requiert
l'agent `auditeur_finances` (VPS/OTOIA, Sprint 5-6), et est une **décision produit Michel**.
**Action demandée à Michel** : trancher la sémantique de la condition #4 du gate —
(a) conserver le champ humain `wag_validated_by` (le module reste sur l'AMENDEMENT), ou
(b) basculer la condition #4 sur la décision `APPROVED` signée de l'OTO Auditeur Finances IA
(conforme à votre PRÉCISION), l'humain WAG devenant fallback « dernier recours » (OFAC/PEP/
recours). Le module sera re-généré selon l'arbitrage. `run_ci.sh` : **32 PASS** inchangé.
## Addendum · Sessions `180728` → `200738` · Fermeture audit 8 directives Michel + couple PLANPOINT↔spec (2 éditions doc, 0 prod)
Rattrapage de ce canal stakeholder, qui s'était arrêté à `173726` (3 sessions de retard sur
l'`activity_log`). Toutes vérifications pures ou éditions **doc** — **0 fichier de production
modifié**, launch-readiness **inchangé** (voir bloc sourcé infra).
- **`180728` · 4e couple `directive-vs-implementation` (`DIRECTIVE_PIE``pie/manifest`)** —
CLEAN. Byte-repro vert, 10 invariants verts, fidélité directive→spec totale. Attributions
`module` vérifiées **sémantiquement** (au-delà de l'inv 6 dir-existence) : 3 exactes
(site→publiciste · kit→bancable · app→mobile). **SIGNAL Michel** : le downstream `contrats`
est marqué gated→`legal/confotur`, mais confotur produit le **DocType CONFOTUR (Ley 158-01)**,
**pas** Promesa/Fideicomiso/HOA — **aucun module ne génère** ces trois → couverture
sur-affirmée = **arbitrage périmètre produit**, pas une correction (spec = autorité, #5).
- **`190730` + `193734` · couple PLANPOINT↔spec + fermeture des 8 directives (2 éditions doc)** —
(a) `specs/CHOISIR_MON_UNITE_SPEC.md` : ajout d'un encart *Arbitrage rouvert* surfaçant **2
contradictions dures** avec la directive `PLANPOINT_STYLE` (snapshot Michel plus récent), sans
flipper aucune décision (spec = autorité). (b) `README.md` : index rendu **symétrique**
(lien directive→spec ajouté). Les 3 directives restantes (ARCHIVES/RENDUS/UNBLOCK) = snapshots
runtime/VPS hors périmètre (#8), correctement bucket-2, marques RENDUS **7/7** conformes
`pie_manifest.json`. **Audit des 8 directives Michel désormais complet.**
- **`200738` (session courante) · re-vérification indépendante du couple PLANPOINT↔spec** —
les **2 contradictions** de l'encart sont **confirmées RÉELLES** contre la directive : Décision 4
spec = **DocuSign** (SaaS externe, l. 58/89/112) ↔ directive §7 = **OTO Sign™** (natif) ;
Décision 5 spec = **comparateur NON** (l. 59/90/119) ↔ directive §6 = **« max 3 unités »**
(l. 48/86). Aucun faux-positif inséré → **édition `a9e9ee9` factuellement saine, 0 correction**.
**⚠️ Renforcement du SIGNAL à Michel (nouvel angle, non tranché).** L'arbitrage Décision 4
n'est **pas neutre** vis-à-vis du mandat : `CLAUDE.md` #1 impose « ERPNext natif = priorité
absolue **avant tout outil externe** ». **OTO Sign™** (directive, produit natif) est donc
**plus aligné** sur le mandat que **DocuSign** (spec, SaaS externe). La directive étant aussi
la plus **récente** (08-03 vs spec 07-28), les deux axes (récence + conformité #1) pointent
vers OTO Sign™ — mais la spec reste l'autorité du module tant que Michel n'a pas tranché.
Reste une **décision produit**, pas une édition (spec = autorité, #5).
**État launch-readiness · fin de journée (inchangé · 100 % sourcé — recompute indépendant)** :
- Qualité 4Big : **24/24** modules (`quality_report.json.coverage` : `registry=ci=24` · `missing_*=[]`)
- Régression : **624/624** tests · 24/24 suites (`regression_run.json.totals`)
- Recette (acceptance) : **15/15** entrées (`acceptance_matrix.json.matrix`)
- Gate CI local : **32 PASS · 0 FAIL · 0 SKIP** (`run_ci.sh`)
Hors périmètre (inchangé · #8) : déploiement VPS, builds/submit EAS, bench migrate, voix Amélie.
@@ -0,0 +1,126 @@
# Daily Report · 2026-08-04 · Claude Code DTP Worker
**Session** : `20260804_221055`
**Phase roadmap** : Sprint 8 · *Launch + Buffer* — « Regression tests exhaustifs »
(QA) + buffer corrections. Sprints 1→7 livrés côté repo, gatés et verts ; le reste
(déploiement production réel, builds EAS, dépôts ONAPI) est **hors périmètre worker**
(VPS `153.75.250.214` · `CLAUDE.md` #8).
## Tâche exécutée
**Passe de vérification d'intégrité inter-artefacts — 5 surfaces (dont 3 neuves au
compte courant 24 modules / 32 jobs) → CLEAN, 0 édition de production.**
Nouveau jour de fenêtre (le `2026-08-03` a été clos à l'addendum `180728→200738`).
Les surfaces de dérive usuelles étant saturées (re-scan = redondant #5), cette
session cible en priorité les surfaces qui **auraient pu dériver avec le passage
récent 22→24 modules** (la seule dérive réelle des dernières ~48 h) + des surfaces
de **correctness numérique** jamais explicitement caractérisées.
## Réalisé — 5 surfaces auditées, 0 dérive, 0 fichier de production modifié
1. **Docstring-vs-CODE / comptes de sortie** (~25 générateurs, agent Explore) :
docstring ↔ README ↔ fichiers réellement écrits → **0 mismatch présent-tense**.
2. **Correctness `commissions`** (neuf) : `taux_pct: null` par design (#6) →
`commission=None` tant qu'un opérande manque → **aucune réconciliation matérialisée
ne peut dériver**. Non-candidate caractérisée.
3. **Réconciliation de compte au point 24** (neuf) : `quality_report` 24 · `coverage`
`ci==registry==24` · `regression` 24 suites/624 · `acceptance` 15/15 · `gate.needs`
32 → **tous concordants** ; la table 08-03 (`100714`) les porte déjà.
4. **`ci_job` recette → `gate.needs`** au point 24/32 (neuf) : **36 refs · 0 orphelin**,
les 2 nouveaux modules (financement/pie) présents, 8 jobs non-cités = contrôles
d'infra CI (absence **par design**).
5. **Couverture `ci/README.md`** (neuf) : §1 = table des 7 gates statiques par design ;
suites de module **dérivées de `gate.needs` à l'exécution** + gatées par
`check-ci-integrity` INV-A → **pas de gap**.
## État launch-readiness · 100 % sourcé (recompute `python3` indépendant · aucun chiffre saisi à la main)
| Dimension | Valeur | Verdict | Source commitée |
|---|---|---|---|
| Qualité 4Big | **24/24 modules à 100/100** (seuil 95 · #5) · `coverage` ci==registry==24 | `PASS` | [`qa/audit_4big/out/quality_report.json`](../qa/audit_4big/out/quality_report.json) |
| Régression (run) | **24 suites · 624 ran · 624 passés · 0/0/0** | `PASS` | [`qa/regression/out/regression_run.json`](../qa/regression/out/regression_run.json) |
| Recette roadmap | **15/15 promesses** `status=in_repo` · **36 `ci_job` → 0 orphelin** | `true` | [`qa/acceptance/out/acceptance_matrix.json`](../qa/acceptance/out/acceptance_matrix.json) |
| Miroir local | `run_ci.sh`**32 PASS · 0 FAIL · 0 SKIP** | vert | [`run_ci.sh`](../../run_ci.sh) |
## Non fait (hors périmètre worker · #8)
Exécution serveur `eas build`/`eas submit` (attend `EAS_TOKEN` + `MOBILE_NATIVE_REPO`
Michel), déploiement production VPS, dépôts ONAPI, création DocTypes ERPNext →
agents Mobile/DevOps/ERPNext Backend. Aucune tâche fonctionnelle **in-repo** ne reste :
la roadmap est intégralement livrée+gatée, et les seuls livrables-fichiers versionnés
in-scope (`mobile-build.yml`) sont déjà faits.
## Prochaine tâche suggérée
Poursuivre la veille d'intégrité (surfaces neuves uniquement · #5) et maintenir la
currency des deux canaux de log. Dès que Michel fournit `EAS_TOKEN`/`MOBILE_NATIVE_REPO`,
le workflow `mobile-build.yml` s'active (jobs actuellement SKIP-safe).
---
## Addendum · Session 224101 · Sonde de dérive non-gatée (4 dimensions neuves) → CLEAN
Seconde session du 08-04. Sonde ciblée sur **4 dimensions de dérive non couvertes par
la CI et jamais caractérisées** (orthogonales aux classes déjà saturées) :
| Dimension sondée | Résultat | Évidence |
|---|---|---|
| Cross-refs entre fiches AGENT.md | **CLEAN** · 0 nom pendant | tous les agents cités existent sous `03_agents/` |
| Dates/session-ids (READMEs, specs) | **CLEAN** · 0 date future | specs citent 07-28 / 08-03 (passé) |
| Contacts d'escalade (tél + email) | **CLEAN** · faux-positif écarté | tél `+18296296385` ×15 identiques · email `mroy@worldactivitiesrd.com` ×1 (roadmap L91, à sa place) |
| Specs ↔ répertoires deliverables | **CLEAN** · 0 orphelin | chaque module spec a son dossier `05_deliverables_mvp/*` |
**0 édition de production** (la seule anomalie remontée par l'agent — email en occurrence
unique — était un **faux-positif** : occurrence unique ≠ contradiction ; anti-invention #6).
`./run_ci.sh` inchangé : **32 PASS · 0 FAIL · 0 SKIP**. Note d'intégrité ajoutée pour
pré-empter le re-flag du contact d'escalade (prose non gatée).
---
## Addendum · Session 231101 · Attestation indépendante des 4 chiffres launch-readiness → CLEAN
Troisième session du 08-04. Plutôt que re-sonder des surfaces de dérive (saturées · #5),
cette passe **atteste** que les 4 chiffres publiés au stakeholder plus haut ne sont pas
un simple écho de la table mais **rechargent à l'identique depuis les artefacts commités**
via un recompute `python3` indépendant (aucune valeur saisie à la main · #6) :
| Dimension | Recompute indépendant (ce jour) | = Table publiée ? |
|---|---|---|
| Qualité 4Big | `quality_report.json` : 24 modules · **tous == 100** · `coverage.ok=true` · `ci==registry==24` · 0 manquant | ✅ identique |
| Régression (run) | `regression_run.json.totals` : **24 suites · 624 ran · 624 passed · 0/0/0** · verdict `PASS` | ✅ identique |
| Recette roadmap | `acceptance_matrix.json` : **15/15 lignes `status=in_repo`** · verdict `true` · **`ci_job` ×36** (niché dans `matrix[].modules[]`, pas au niveau ligne) → concorde avec « 36 refs » | ✅ identique |
| Miroir local | `./run_ci.sh`**32 PASS · 0 FAIL · 0 SKIP** | ✅ identique |
**Confirmé au passage** que l'artefact d'EXÉCUTION `regression_run.json` (exclu de
`check_artifacts` car non issu de `build`) est bien protégé par son gate dédié
`ci/check_regression.sh` (régénère `run` frais + exige l'identité byte-for-byte) — donc
les totaux 624 ne peuvent pas se périmer en silence. **0 édition de production · 0 dérive.**
---
## Addendum · Session 234104 · Durcissement du gating d'activation `mobile-build.yml` → 1 édition CI in-scope
Quatrième session du 08-04. Après 3 passes read-only CLEAN, changement de registre :
**correctness d'un artefact CI in-scope à impact futur réel** plutôt qu'un 4e audit
redondant. Cible = le workflow `mobile-build.yml` que ce rapport désigne comme prochaine
tâche (s'active dès `EAS_TOKEN` fourni). Un agent Explore a confirmé **0 bug de logique**
côté générateurs Python ; le défaut réel était dans le YAML CI.
**Défaut latent corrigé.** Les jobs `build-ios`/`build-android` gataient sur
`if: secrets.EAS_TOKEN != '' …` **au niveau job**, où le `secrets` context n'est **pas**
garanti (table d'availability GitHub : `jobs.<id>.if` = github/needs/vars/inputs/matrix
seulement). Risque : le jour où Michel fournit un `EAS_TOKEN` valide, les builds
resteraient **SKIP en silence**. Corrigé via le pattern canonique **preflight-output**
(le secret est lu dans un step, exposé en `needs.preflight.outputs.has_token/has_repo`,
gate consommé en `if:` — portable GitHub ET Gitea/act).
| Contrôle | Résultat |
|---|---|
| YAML `mobile-build.yml` | **valide** (`yaml.safe_load`) |
| `secrets.` restant dans un `if:` de job | **0** ✓ |
| `preflight.outputs` + `needs: preflight` (ios/android) | présents ✓ |
| Contrat observable (preflight vert · builds SKIP sans token) | **inchangé** |
| Gate de merge touché ? | **non** (workflow hors `gate.needs`) · `run_ci.sh` **32 PASS** |
**Impact stakeholder.** Quand Michel ajoutera le secret `EAS_TOKEN` (expo.dev · ~2 min)
+ la variable `MOBILE_NATIVE_REPO`, les builds EAS se déclencheront désormais de façon
**déterministe** — plus de dépendance à un comportement de contexte ambigu selon le moteur
Actions. **0 chiffre inventé · 0 credential fabriqué.**
@@ -0,0 +1,138 @@
# Daily Report · 2026-08-05 · Claude Code DTP Worker
**Session** : `20260805_004112`
**Phase roadmap** : Sprint 8 · *Launch + Buffer* — « Monitoring / regression
exhaustifs » (DevOps/QA) + buffer. Sprints 1→7 livrés côté repo, gatés et verts ;
le reste (déploiement production réel, builds EAS, dépôts ONAPI) est **hors périmètre
worker** (VPS `153.75.250.214` · `CLAUDE.md` #8).
## Contexte — jalon CI franchi la veille au soir (à consigner côté stakeholder)
La session `20260805_001109` a livré le **8ᵉ gate statique** `ci/check_mobile_workflow.sh`
et l'a câblé dans `gate.needs` : la **structure** du second workflow `mobile-build.yml`
(hors gate de merge, mais dont un défaut passait VERT en silence) est désormais **opposable
au merge**. Le décompte du gate de merge est donc passé de **32 → 33 jobs**. Le canal
stakeholder s'arrêtait au `2026-08-04` (« 32 PASS ») : ce rapport le porte au point courant.
## Tâche exécutée — currency du canal + attestation indépendante du point courant · 0 édition de production
Aucune tâche fonctionnelle **in-repo** ne reste (roadmap intégralement livrée+gatée ;
seul livrable-fichier in-scope restant, `mobile-build.yml`, déjà fait ET désormais gaté).
La valeur honnête du jour : **rendre le canal roadmap fidèle au jalon 33 jobs** et
**réattester** que les chiffres publiés rechargent à l'identique depuis les artefacts
commités (recompute `python3` indépendant · aucune valeur saisie à la main · #6).
## État launch-readiness · 100 % sourcé (recompute indépendant ce jour · aucun chiffre saisi à la main)
| Dimension | Valeur | Verdict | Source commitée |
|---|---|---|---|
| Qualité 4Big | **24/24 modules à 100/100** (seuil 95 · #5) · `coverage.ok=true` · ci==registry==24 · 0 manquant | `PASS` | [`qa/audit_4big/out/quality_report.json`](../qa/audit_4big/out/quality_report.json) |
| Régression (run) | **24 suites · 624 ran · 624 passés · 0 fail / 0 err / 0 skip** | `PASS` | [`qa/regression/out/regression_run.json`](../qa/regression/out/regression_run.json) |
| Recette roadmap | **15/15 promesses** `status=in_repo` · **36 `ci_job` → 0 orphelin** | `true` | [`qa/acceptance/out/acceptance_matrix.json`](../qa/acceptance/out/acceptance_matrix.json) |
| Miroir local | `run_ci.sh`**33 PASS · 0 FAIL · 0 SKIP** | vert | [`run_ci.sh`](../../run_ci.sh) |
| Gate de merge | **33 jobs** dérivés de `gate.needs` = **8 gates statiques `ci/*.sh` + 25 suites de module** | vert | [`.gitea/workflows/ci.yml`](../../.gitea/workflows/ci.yml) · [`ci/check_mobile_workflow.sh`](../../ci/check_mobile_workflow.sh) |
> Le décompte 33 est **dérivé à l'exécution** (`run_ci.sh --list`), jamais figé dans une
> prose : ajouter/retirer un gate le met à jour sans édition (anti-invention #6). Les
> « 32 » des logs antérieurs sont des **snapshots datés** (état vrai à l'époque), pas des
> prétentions présent-tense — ils restent tels quels (classe *historical-repro = KEEP*).
## Attestation (recompute `python3` vs artefacts commités — pas un écho de la table)
| Dimension | Recompute indépendant ce jour | = Table publiée ? |
|---|---|---|
| Qualité 4Big | `quality_report.json` : 24 modules · **tous == 100** · `coverage.ok=true` · ci==registry==24 · verdict `PASS` | ✅ identique |
| Régression (run) | `regression_run.json.totals` : **suites 24 · ran 624 · passed 624 · 0/0/0** · verdict `PASS` | ✅ identique |
| Recette roadmap | `acceptance_matrix.json` : **15/15 `status=in_repo`** · verdict `True` · **`ci_job` ×36** (niché dans `matrix[].modules[]`) | ✅ identique |
| Structure CI | `run_ci.sh --list` : **33 jobs = 8 statiques + 25 suites** · `ls ci/*.sh` (hors `lib.sh`) = **8** | ✅ concordant |
## Non fait (hors périmètre worker · #8)
Exécution serveur `eas build`/`eas submit` (attend `EAS_TOKEN` + `MOBILE_NATIVE_REPO`
fournis par Michel), déploiement production VPS, dépôts ONAPI, création DocTypes ERPNext
→ agents Mobile/DevOps/ERPNext Backend. Le workflow `mobile-build.yml` est SKIP-safe
tant que le token est absent (jobs de build gatés sur `preflight.outputs.has_token`) —
et sa structure est maintenant verrouillée par `check-mobile-workflow` (MOB-1..4), donc
la garantie « pas de SKIP silencieux le jour de l'activation » ne peut plus régresser.
## Prochaine tâche suggérée
Maintenir la currency des deux canaux de log et poursuivre la veille d'intégrité sur
surfaces **neuves uniquement** (#5 — les surfaces de dérive usuelles sont saturées). Dès
que Michel fournit `EAS_TOKEN`/`MOBILE_NATIVE_REPO`, `mobile-build.yml` s'active de façon
déterministe (gating portable preflight-output, désormais gaté au merge).
---
## Mise à jour fin de journée · session `20260805_054200` — les DEUX axes de « dents » clos
Ce rapport matinal (session `004112`) s'arrêtait au jalon **33 jobs**. Plus tard le même
jour, la série d'attestation *teeth* (mutation-testing) a été **close sur ses deux axes**.
Jalon launch-readiness distinct des tables ci-dessus : celles-ci prouvent que la CI est
**verte** (les gates PASSent, les artefacts rechargent) ; les attestations *teeth* prouvent
que ces gates et suites **échouent bien quand ils le doivent** — un gate ou une suite
toujours-vert serait un **faux vert** invisible aux tables de currency.
| Axe *teeth* | Portée | Résultat | Preuve commitée |
|---|---|---|---|
| **Gates statiques** (mutation de suite complète) | **8/8** (`ls ci/*.sh` hors `lib.sh` = 8) | **8 BITES · 0 TOOTHLESS** | commit `9fe8bf1` (`20260805_031134`) |
| **Suites de module** (dérive de LOGIQUE générateur, jamais écho d'artefact) | **25/25** (= les 25 suites de `gate.needs`) via 4 batches **6+6+6+7** | **25 BITES · 0 TOOTHLESS** | commits `98f3f56` · `d25da28` · `e38dd93` · `986d293` (`034144``051154`) |
**Ce que « BITE » garantit au stakeholder.** Chaque suite de module a été mutée sur la
LOGIQUE de son générateur (ex. `commission = base × taux`, e-NCF `zfill(10)`, dédup SEO,
gating `_statut`, séparateur d'options CONFOTUR) et **doit** sortir NONZERO — prouvant que
la suite contraint le comportement, et ne recharge pas `out/*.json` contre lui-même. Une
suite édentée passerait VERT sur une régression réelle = **faux vert Sprint 8**. Les 25
sont désormais **prouvées mordantes**.
**Hygiène du harnais (inchangée).** Mutations jouées dans `/tmp` en lecture seule sur copie,
restauration byte-exacte par `cp`+`cmp` (jamais `git clean` · #1), arbre de travail
**pristine** après coup. **0 gate ajouté** (#5 — attester ≠ multiplier les gates), **0
chiffre saisi à la main** (#6 — comptes dérivés de `ls`/`gate.needs`), **0 édition de
production**, **aucune commande VPS** (#8).
**Currency structurelle inchangée** : `run_ci.sh`**33 PASS · 0 FAIL · 0 SKIP** (aucun
fichier gaté touché aujourd'hui ; ce rapport est le seul livrable de la session `054200`).
---
## Mise à jour fin de journée · session `20260805_144304` — currency du canal + **incident d'intégrité corrigé le jour même**
Ce rapport s'arrêtait à la session `054200`. **Sept commits** ont suivi dans la journée ;
un seul est **matériel pour le stakeholder** — un tree commité momentanément **RED**, capté
et corrigé le jour même. Le reste est de la veille d'intégrité en lecture seule. Toutes les
tables ci-dessus **recomputent à l'identique ce jour** (recompute `python3` indépendant · #6).
### Incident d'intégrité (capté + corrigé le jour même)
| Événement | Commit | État du tree commité | Résolution |
|---|---|---|---|
| `test_audit_4big.py` figeait `totals["pass"] = 18 - fail` (périmètre réel = 24 modules) → correction annoncée « 33 PASS » **mais tree commité RED 32 PASS·1 FAIL** (le gate `constraints-guard` mordait sur le tree lui-même) | `127ef6f` (`124241`) | **RED** (32 PASS · 1 FAIL) | — |
| Régression captée et corrigée : constante de fixture dérivée du vrai `bad["modules"]` (`pass=len-fail`, min/max=min/max scores) → rapport réellement cohérent en interne, **seul INV7 mord** ; assertion resserrée `any("< seuil")``all("INV7") + any("< seuil")` (isolation prouvée) | `1321983` (`131244`) | **VERT** (33 PASS · 0 FAIL) | ✅ tree revenu VERT |
> **Ce que l'incident démontre au stakeholder** : le gate `guard_constraints` **mord sur
> le tree commité lui-même** (pas seulement en pré-commit local) — un faux « 33 PASS »
> annoncé sans le tree correspondant a été **opposable et rattrapé sous 50 min**. La
> discipline de gate a fonctionné ; le canal l'enregistre plutôt que de laisser croire à
> une journée VERTE ininterrompue. HEAD courant (`f311a1b`) : `run_ci.sh` → **33 PASS**.
### Veille d'intégrité en lecture seule (0 édition de production · #5)
| Sweep | Commit | Portée | Résultat |
|---|---|---|---|
| Docstring-vs-code | `48b19b9` (`134254`) | 26 modules (docstrings ↔ comportement réel du code) | **CLEAN** |
| Forward-compat warnings | `f311a1b` (`141301`) | 25 suites de `gate.needs` rejouées sous `-W error` (Deprecation/Syntax/Pending/Resource) sous Python 3.10.12 | **CLEAN 25/25 · 624 tests · 0 warning latent** |
| Déterminisme hash-seed | `151312` | Tous les générateurs `build` (57 artefacts) rejoués sous 5 `PYTHONHASHSEED` (`1/7/42/1000/random`) + `regression_run.json` sous 6 rejeux → `diff` vs commité | **CLEAN · 57 byte-identiques · 0 dépendance à l'ordre de hash** → gates `check_artifacts`/`check_regression` robustes au seed randomisé du runner |
| Indépendance locale / timezone | `154314` | 21 générateurs `build` × 5 combos `LANG`/`LC_ALL`/`TZ` (`C·UTC``de_DE·Kolkata``POSIX·Kiritimati`) = **105 rejeux** `diff` vs commité ; TZ empiriquement exercé (5 fuseaux) + audit code (0 `locale.setlocale``json` reste `'.'` ; 2 seuls lecteurs d'horloge sans `out/` commité et en `timezone.utc`) | **CLEAN · 0 dérive/105** → runner robuste sous toute locale/fuseau |
### Ré-attestation du point courant (recompute indépendant ce jour, HEAD `f311a1b`)
| Dimension | Recompute `python3` | = Table publiée ? |
|---|---|---|
| Qualité 4Big | `quality_report.json` : **24 modules · tous == 100** · `coverage.ok=true` · verdict `PASS` | ✅ identique |
| Régression (run) | `regression_run.json.totals` : **suites 24 · ran 624 · passed 624 · 0/0/0** | ✅ identique |
| Structure CI | `run_ci.sh`**33 PASS · 0 FAIL · 0 SKIP** = 8 statiques + 25 suites | ✅ concordant |
**Portée / anti-churn.** **0 fichier de production édité**, **0 artefact reconstruit**,
**0 gate ajouté** (#5), **0 chiffre saisi à la main** (#6 — 24/624/33 dérivés des artefacts
et de `run_ci.sh`), **aucune commande VPS** (#8). Seule édition : ce rapport + le journal.
@@ -47,9 +47,9 @@ chiffrée dans le catalogue de confirmations.
| 1 | vps | Prérequis (modules custom OTOV7 + spec RBAC) | `rbac` | — |
| 2 | vps | DocTypes custom | `crm/dossier_vente`, `legal/confotur` | 1 |
| 3 | worker+vps | RBAC (Role + DocPerm + UP + Role Profile) | `rbac/fixtures_gen`, `rbac/userperm_gen`, `rbac/roleprofile_gen`, `rbac/apply_plan` | 2 |
| 4 | worker+vps | Workflow vente + règles métier | `crm/workflow_vente`, `crm/commissions`, `fiscal/ecf_dgii` | 2, 3 |
| 4 | worker+vps | Workflow vente + règles métier | `crm/commissions`, `crm/financement_bancaire`, `crm/workflow_vente`, `fiscal/ecf_dgii` | 2, 3 |
| 5 | worker+vps | Frontend desk (Workspaces + chat OTOIA) + config mobile | `frontend/portails`, `frontend/chat_otoia`, `mobile/app_config` | 3 |
| 6 | worker+vps | Contenu & publication | `publiciste`, `faisabilite/generator`, `faisabilite/bancable`, `seo`, `demo/scenarios` | 4, 5 |
| 6 | worker+vps | Contenu & publication | `demo/scenarios`, `faisabilite/bancable`, `faisabilite/generator`, `pie/manifest`, `publiciste`, `seo` | 4, 5 |
| 7 | vps | Vérification QA post-déploiement | `qa/acceptance`, `qa/audit_5d`, `qa/audit_4big`, `qa/regression` | 4, 5, 6 |
**Confirmations préalables VPS** (consolidées, sourcées) : `custom_modules`,
+1 -1
View File
@@ -49,7 +49,7 @@ de dépendances, à consommer par ces agents.
| Page projet (site) | [`publiciste`](../../publiciste/README.md) |
| Section app mobile | [`mobile/app_config`](../../mobile/app_config/README.md) |
Les 9 autres (brand book, logos, brochures PDF, plans, rendus, fiche unité, BIM,
Les 9 autres (brand book, brochures PDF, plans, rendus, fiche unité, BIM,
slogan, calendrier, trackers chantier) sont marqués `a_construire` (P1-P4 de la
directive) — aucun sur-engagement.
@@ -280,7 +280,7 @@ def _validate_or_die(bundle: dict) -> None:
sys.exit(1)
def cmd_build(args) -> None:
def cmd_build(args) -> int:
bundle = _build_bundle()
_validate_or_die(bundle)
out_dir = args.out or _DEFAULT_OUT
@@ -297,17 +297,19 @@ def cmd_build(args) -> None:
f"hors-périmètre sourcés.")
print(f"{os.path.join(out_dir, 'acceptance_matrix.json')}")
print(f"{os.path.join(out_dir, 'MANIFEST.json')}")
return 0
def cmd_validate(args) -> None:
def cmd_validate(args) -> int:
bundle = _build_bundle()
_validate_or_die(bundle)
cov = bundle["manifest"]["coverage"]
print(f"✅ Matrice valide — bijective={cov['bijective']} · "
f"partition_ok={cov['partition_ok']} · verdict={bundle['verdict']}.")
return 0
def main(argv=None) -> None:
def main(argv=None) -> int:
p = argparse.ArgumentParser(
prog="acceptance_gen",
description="Matrice d'acceptation / traçabilité MVP (roadmap → preuve).")
@@ -322,8 +324,8 @@ def main(argv=None) -> None:
v.set_defaults(func=cmd_validate)
args = p.parse_args(argv)
args.func(args)
return args.func(args)
if __name__ == "__main__":
main()
raise SystemExit(main())
@@ -41,8 +41,7 @@
"rbac/fixtures_gen",
"rbac/userperm_gen",
"rbac/roleprofile_gen",
"rbac/apply_plan",
"faisabilite/generator"
"rbac/apply_plan"
],
"evidence_artifacts": [],
"out_of_scope": [
@@ -59,7 +58,7 @@
"roadmap_line": 46,
"statement": "P07 Aqua Terra faisabilité complète auto-générée en <1h",
"status": "in_repo",
"evidence_modules": ["faisabilite/bancable"],
"evidence_modules": ["faisabilite/bancable", "faisabilite/generator"],
"evidence_artifacts": [],
"out_of_scope": [
{
@@ -33,7 +33,6 @@
{
"sprint": "S2",
"expected": [
"faisabilite/generator",
"publiciste",
"rbac",
"rbac/apply_plan",
@@ -42,7 +41,6 @@
"rbac/userperm_gen"
],
"cited": [
"faisabilite/generator",
"publiciste",
"rbac",
"rbac/apply_plan",
@@ -57,10 +55,12 @@
{
"sprint": "S3",
"expected": [
"faisabilite/bancable"
"faisabilite/bancable",
"faisabilite/generator"
],
"cited": [
"faisabilite/bancable"
"faisabilite/bancable",
"faisabilite/generator"
],
"missing": [],
"extra": [],
@@ -62,11 +62,6 @@
"module": "rbac/apply_plan",
"ci_job": "rbac-applyplan-tests",
"gated": true
},
{
"module": "faisabilite/generator",
"ci_job": "faisabilite-gen-tests",
"gated": true
}
],
"artifacts": [],
@@ -76,7 +71,7 @@
"source": "CLAUDE.md #8 (VPS) · #1 (ERPNext natif = priorité — import via bench par agent Backend)"
}
],
"n_modules": 7,
"n_modules": 6,
"all_modules_gated": true,
"all_artifacts_exist": true,
"has_evidence": true
@@ -93,6 +88,11 @@
"module": "faisabilite/bancable",
"ci_job": "bancable-tests",
"gated": true
},
{
"module": "faisabilite/generator",
"ci_job": "faisabilite-gen-tests",
"gated": true
}
],
"artifacts": [],
@@ -102,7 +102,7 @@
"source": "CLAUDE.md #8 (VPS) — le rendu 3D/BIM tourne hors repo (bim-cloud.otov7.com)"
}
],
"n_modules": 1,
"n_modules": 2,
"all_modules_gated": true,
"all_artifacts_exist": true,
"has_evidence": true
+1 -1
View File
@@ -65,7 +65,7 @@ Publiciste** (zéro `pip`, runner Gitea sans réseau · CLAUDE.md #2).
## Résultat courant
**Verdict `PASS`****23/23** modules à **100/100** (min 100), couverture
**Verdict `PASS`****24/24** modules à **100/100** (min 100), couverture
**100 %** des livrables gated. L'audit servira surtout de **gate anti-régression** :
retirer des tests, casser un `MANIFEST` ou supprimer un schéma fait chuter le
module sous 95 → `FAIL`.
@@ -40,42 +40,6 @@
}
],
"modules": [
{
"id": "faisabilite-generator",
"path": "faisabilite/generator",
"sprint": "S2",
"archetype": "data_room",
"applicable_weight": 80,
"earned_weight": 80,
"score": 100,
"verdict": "PASS",
"checks": [
{
"criterion": "DOC",
"weight": 20,
"passed": true,
"evidence": "README.md (4797 octets)"
},
{
"criterion": "CONTRAT",
"weight": 20,
"passed": true,
"evidence": "brief.schema.json"
},
{
"criterion": "TESTS",
"weight": 25,
"passed": true,
"evidence": "16 méthodes test_* dans 1 fichier(s) (seuil 8)"
},
{
"criterion": "CLI",
"weight": 15,
"passed": true,
"evidence": "faisabilite_gen.py (argparse + __main__)"
}
]
},
{
"id": "publiciste",
"path": "publiciste",
@@ -340,6 +304,42 @@
}
]
},
{
"id": "faisabilite-generator",
"path": "faisabilite/generator",
"sprint": "S3",
"archetype": "data_room",
"applicable_weight": 80,
"earned_weight": 80,
"score": 100,
"verdict": "PASS",
"checks": [
{
"criterion": "DOC",
"weight": 20,
"passed": true,
"evidence": "README.md (4797 octets)"
},
{
"criterion": "CONTRAT",
"weight": 20,
"passed": true,
"evidence": "brief.schema.json"
},
{
"criterion": "TESTS",
"weight": 25,
"passed": true,
"evidence": "16 méthodes test_* dans 1 fichier(s) (seuil 8)"
},
{
"criterion": "CLI",
"weight": 15,
"passed": true,
"evidence": "faisabilite_gen.py (argparse + __main__)"
}
]
},
{
"id": "crm-commissions",
"path": "crm/commissions",
@@ -774,7 +774,7 @@
"criterion": "DOC",
"weight": 20,
"passed": true,
"evidence": "README.md (3900 octets)"
"evidence": "README.md (3901 octets)"
},
{
"criterion": "CONTRAT",
@@ -858,7 +858,7 @@
"criterion": "DOC",
"weight": 20,
"passed": true,
"evidence": "README.md (5468 octets)"
"evidence": "README.md (5512 octets)"
},
{
"criterion": "CONTRAT",
@@ -900,7 +900,7 @@
"criterion": "DOC",
"weight": 20,
"passed": true,
"evidence": "README.md (4022 octets)"
"evidence": "README.md (4015 octets)"
},
{
"criterion": "CONTRAT",
@@ -63,7 +63,7 @@
"modules": [
{ "id": "publiciste", "path": "publiciste", "sprint": "S2", "archetype": "parser", "source": "roadmap Sprint 2 · Publiciste (parser faisabilité → projets_master.json) · ci.yml publiciste-tests" },
{ "id": "rbac", "path": "rbac", "sprint": "S2", "archetype": "contract", "source": "roadmap Sprint 2 · RBAC 50 rôles (contrat rbac_50_roles.json) · ci.yml rbac-tests" },
{ "id": "faisabilite-generator", "path": "faisabilite/generator", "sprint": "S2", "archetype": "data_room", "source": "roadmap Sprint 2 · Faisabilité générateur 4 volets → data_room/PXX · ci.yml faisabilite-gen-tests" },
{ "id": "faisabilite-generator", "path": "faisabilite/generator", "sprint": "S3", "archetype": "data_room", "source": "roadmap Sprint 3 · Faisabilité générateur 4 volets → data_room/PXX · ci.yml faisabilite-gen-tests" },
{ "id": "rbac-fixtures", "path": "rbac/fixtures_gen", "sprint": "S2", "archetype": "generator", "source": "roadmap Sprint 2 · fixtures Role + Custom DocPerm · ci.yml rbac-fixtures-tests" },
{ "id": "rbac-userperm", "path": "rbac/userperm_gen", "sprint": "S2", "archetype": "generator", "source": "roadmap Sprint 2 · plan User Permission · ci.yml rbac-userperm-tests" },
{ "id": "rbac-roleprofile", "path": "rbac/roleprofile_gen", "sprint": "S2", "archetype": "generator", "source": "roadmap Sprint 2 · Role Profile par portail · ci.yml rbac-roleprofile-tests" },
@@ -318,10 +318,19 @@ class InvariantGuardTest(unittest.TestCase):
m["verdict"] = "PASS" if m["score"] >= 95 else "FAIL"
bad["totals"]["fail"] = sum(1 for x in bad["modules"]
if x["verdict"] != "PASS")
bad["totals"]["pass"] = 18 - bad["totals"]["fail"]
# Totaux dérivés du vrai périmètre (pas d'entier figé) → le rapport
# reste cohérent en interne : INV8 (pass+fail=modules, min/max) ne doit
# PAS mordre, seul INV7 le doit.
bad["totals"]["pass"] = len(bad["modules"]) - bad["totals"]["fail"]
_scores = [x["score"] for x in bad["modules"]]
bad["totals"]["min_score"] = min(_scores)
bad["totals"]["max_score"] = max(_scores)
bad["verdict"] = "FAIL"
errs = gen.check_invariants(bad, self.spec)
# Le rapport est cohérent en interne mais porte un FAIL → INV7 le signale.
# Le rapport est cohérent en interne mais porte un module sous le seuil
# → INV7 (et lui seul) le signale.
self.assertTrue(errs, "INV7 aurait dû signaler le module sous seuil")
self.assertTrue(all("INV7" in e for e in errs), errs)
self.assertTrue(any("< seuil 4Big" in e for e in errs), errs)
def test_self_module_in_scope_is_caught(self):
+9 -2
View File
@@ -51,9 +51,16 @@ python3 regression_gen.py run # → out/regression_run.json (COMMIT
```
`run` renvoie un code de sortie ≠ 0 si **une seule** suite est rouge → utilisable
comme garde de release. Sa sortie est **byte-déterministe** elle ne porte
comme garde de release. Sa sortie est **byte-déterministe** : (a) elle ne porte
**aucun** horodatage / hôte / durée / chemin absolu (`path` relatif, compteurs
`ran/passed/failures` seuls) : deux `run` successifs sont **byte-identiques**.
`ran/passed/failures` seuls) ; (b) chaque suite est exécutée sous `python -S`
(sans `site-packages`), donc l'unique paquet tiers du corpus — l'oracle
**optionnel** `jsonschema` (toujours gardé par `skipUnless`) — est neutralisé,
exactement comme sur le runner Gitea pip-less. Sans (b), la matrice divergeait
selon que `jsonschema` était `pip install`é ou non (0 vs 17 `skipped`) → un
**faux-vert** : `check_regression` vert en local mais ROUGE sur le runner. Avec
les deux, deux `run` sont **byte-identiques** quel que soit l'environnement
(17 oracles ignorés ; les validateurs maison couvrent les mêmes contrats).
`out/regression_run.json` est donc **commité** et sert de **baseline** au gate CI
`ci/check_regression.sh`, qui régénère un `run` frais et exige l'**identité
byte-for-byte** avec le fichier commité (+ verdict `PASS`) — c'est ce qui empêche
@@ -8,10 +8,10 @@
"path": "crm/commissions",
"expected_methods": 25,
"ran": 25,
"passed": 25,
"passed": 24,
"failures": 0,
"errors": 0,
"skipped": 0,
"skipped": 1,
"ok": true,
"returncode": 0
},
@@ -20,10 +20,10 @@
"path": "crm/dossier_vente",
"expected_methods": 31,
"ran": 31,
"passed": 31,
"passed": 30,
"failures": 0,
"errors": 0,
"skipped": 0,
"skipped": 1,
"ok": true,
"returncode": 0
},
@@ -44,10 +44,10 @@
"path": "crm/workflow_vente",
"expected_methods": 25,
"ran": 25,
"passed": 25,
"passed": 24,
"failures": 0,
"errors": 0,
"skipped": 0,
"skipped": 1,
"ok": true,
"returncode": 0
},
@@ -80,10 +80,10 @@
"path": "faisabilite/bancable",
"expected_methods": 22,
"ran": 22,
"passed": 22,
"passed": 21,
"failures": 0,
"errors": 0,
"skipped": 0,
"skipped": 1,
"ok": true,
"returncode": 0
},
@@ -92,10 +92,10 @@
"path": "faisabilite/generator",
"expected_methods": 16,
"ran": 16,
"passed": 16,
"passed": 15,
"failures": 0,
"errors": 0,
"skipped": 0,
"skipped": 1,
"ok": true,
"returncode": 0
},
@@ -104,10 +104,10 @@
"path": "fiscal/ecf_dgii",
"expected_methods": 39,
"ran": 39,
"passed": 39,
"passed": 38,
"failures": 0,
"errors": 0,
"skipped": 0,
"skipped": 1,
"ok": true,
"returncode": 0
},
@@ -128,10 +128,10 @@
"path": "frontend/portails",
"expected_methods": 19,
"ran": 19,
"passed": 19,
"passed": 18,
"failures": 0,
"errors": 0,
"skipped": 0,
"skipped": 1,
"ok": true,
"returncode": 0
},
@@ -140,10 +140,10 @@
"path": "legal/confotur",
"expected_methods": 44,
"ran": 44,
"passed": 44,
"passed": 43,
"failures": 0,
"errors": 0,
"skipped": 0,
"skipped": 1,
"ok": true,
"returncode": 0
},
@@ -159,15 +159,27 @@
"ok": true,
"returncode": 0
},
{
"id": "pie.manifest",
"path": "pie/manifest",
"expected_methods": 20,
"ran": 20,
"passed": 19,
"failures": 0,
"errors": 0,
"skipped": 1,
"ok": true,
"returncode": 0
},
{
"id": "publiciste",
"path": "publiciste",
"expected_methods": 23,
"ran": 23,
"passed": 23,
"passed": 21,
"failures": 0,
"errors": 0,
"skipped": 0,
"skipped": 2,
"ok": true,
"returncode": 0
},
@@ -212,10 +224,10 @@
"path": "rbac",
"expected_methods": 10,
"ran": 10,
"passed": 10,
"passed": 9,
"failures": 0,
"errors": 0,
"skipped": 0,
"skipped": 1,
"ok": true,
"returncode": 0
},
@@ -224,10 +236,10 @@
"path": "rbac/apply_plan",
"expected_methods": 16,
"ran": 16,
"passed": 16,
"passed": 15,
"failures": 0,
"errors": 0,
"skipped": 0,
"skipped": 1,
"ok": true,
"returncode": 0
},
@@ -236,10 +248,10 @@
"path": "rbac/fixtures_gen",
"expected_methods": 11,
"ran": 11,
"passed": 11,
"passed": 10,
"failures": 0,
"errors": 0,
"skipped": 0,
"skipped": 1,
"ok": true,
"returncode": 0
},
@@ -248,10 +260,10 @@
"path": "rbac/roleprofile_gen",
"expected_methods": 11,
"ran": 11,
"passed": 11,
"passed": 10,
"failures": 0,
"errors": 0,
"skipped": 0,
"skipped": 1,
"ok": true,
"returncode": 0
},
@@ -260,10 +272,10 @@
"path": "rbac/userperm_gen",
"expected_methods": 12,
"ran": 12,
"passed": 12,
"passed": 11,
"failures": 0,
"errors": 0,
"skipped": 0,
"skipped": 1,
"ok": true,
"returncode": 0
},
@@ -272,23 +284,23 @@
"path": "seo",
"expected_methods": 36,
"ran": 36,
"passed": 36,
"passed": 35,
"failures": 0,
"errors": 0,
"skipped": 0,
"skipped": 1,
"ok": true,
"returncode": 0
}
],
"totals": {
"suites": 23,
"green": 23,
"suites": 24,
"green": 24,
"red": 0,
"ran": 604,
"passed": 604,
"ran": 624,
"passed": 607,
"failures": 0,
"errors": 0,
"skipped": 0
"skipped": 17
},
"verdict": "PASS"
}
@@ -69,8 +69,11 @@ def build_plan(spec: dict[str, Any]) -> dict[str, Any]:
def run_matrix(spec: dict[str, Any], *, python: str | None = None) -> dict[str, Any]:
"""Exécute réellement toutes les suites du plan et agrège les compteurs.
Byte-déterministe (aucun horodatage/hôte/chemin absolu) → l'artefact
`regression_run.json` EST commité et byte-gaté par `ci/check_regression.sh`."""
Byte-déterministe (aucun horodatage/hôte/chemin absolu ; suites lancées sous
`python -S` → oracle tiers optionnel `jsonschema` neutralisé, cf.
`runner.run_suite`) → l'artefact `regression_run.json` EST commité et
byte-gaté par `ci/check_regression.sh`, identique en local et sur le runner
Gitea pip-less."""
suites = discovery.discover_suites(spec)
discover_cmd = list(spec["discover_cmd"])
# `python3 -m unittest ...` → on retire le binaire, on garde les args.
@@ -10,6 +10,19 @@ des fichiers `tests/test_*.py` aux noms qui se recouvrent → un discover global
in-process provoquerait des collisions de modules. Le subprocess reproduit
fidèlement l'environnement du CI.
Déterminisme cross-environnement (`-S`) : chaque suite est lancée avec l'option
`-S` (interpréteur SANS `site` → aucun `site-packages`/`dist-packages` sur
`sys.path`). C'est le contrat exact du runner Gitea (`.gitea/workflows/ci.yml` :
« stdlib pur, zéro pip » — « L'oracle jsonschema est utilisé s'il est présent,
sinon ignoré »). Sans `-S`, le seul paquet tiers du corpus — l'oracle OPTIONNEL
`jsonschema`, toujours gardé par `skipUnless`/`try…except ImportError` — ferait
diverger la matrice : présent sur un poste dev (`skipped=0`), absent sur le
runner pip-less (`skipped=N`). L'artefact `regression_run.json` commité — que
`ci/check_regression.sh` exige byte-identique au run frais — devenait alors
un FAUX-VERT : vert en local, ROUGE sur le runner. `-S` neutralise l'oracle
partout → la matrice reflète la baseline garantie (stdlib + validateurs maison)
et redevient byte-identique quel que soit ce qui est `pip install`é localement.
`parse_unittest_output` est PUR (str → dict) donc testable sans exécuter quoi que
ce soit — on lui injecte des sorties unittest synthétiques (OK / FAILED / skipped).
"""
@@ -70,7 +83,11 @@ def run_suite(abs_path: str, *, python: str | None = None,
`abs_path` = répertoire du module (contenant `tests/`). Aucune sortie n'est
imprimée ici — l'agrégateur décide de l'affichage."""
py = python or sys.executable
cmd = list(discover_cmd or ["-m", "unittest", "discover", "-s", "tests"])
# `-S` : interpréteur sans `site` → pas de `site-packages` → l'oracle tiers
# optionnel `jsonschema` est absent, exactement comme sur le runner Gitea
# pip-less. Garantit une matrice byte-déterministe indépendante de ce qui est
# `pip install`é en local (cf. docstring du module).
cmd = ["-S", *list(discover_cmd or ["-m", "unittest", "discover", "-s", "tests"])]
tests_dir = os.path.join(abs_path, "tests")
if not os.path.isdir(tests_dir):
return {
@@ -25,9 +25,12 @@ Sous-commandes :
live + verdict ; sort en erreur (code ≠ 0) si une suite est
rouge. Écrit `regression_run.json` : bien qu'il porte les
compteurs réellement exécutés, il ne contient AUCUN
horodatage/hôte/chemin absolu → deux `run` sont
byte-identiques, donc l'artefact EST commité et byte-gaté
par `ci/check_regression.sh` (toute dérive = CI rouge).
horodatage/hôte/chemin absolu, ET chaque suite tourne sous
`python -S` (oracle tiers optionnel `jsonschema` neutralisé,
comme sur le runner pip-less · cf. `reglib/runner.py`) →
deux `run` sont byte-identiques quel que soit l'environnement,
donc l'artefact EST commité et byte-gaté par
`ci/check_regression.sh` (toute dérive = CI rouge).
Toutes les sous-commandes productrices sont déterministes (tri stable, aucun
horodatage) → artefacts diffables + commités + byte-gatés : `regression_plan.json`
+1 -1
View File
@@ -64,7 +64,7 @@ complètes + `x-default` = langue par défaut = `canonical` · toutes les URLs s
## Tests
```bash
python3 -m unittest discover -s tests -v # 36 tests (dont 8 injections négatives)
python3 -m unittest discover -s tests -v # 36 tests (dont 10 injections négatives)
```
## Hors périmètre worker (VPS · #8)
@@ -68,6 +68,21 @@ _Conservées pour traçabilité ; chaque point est résolu par la section
## ✅ DÉCISIONS FINALES MICHEL (2026-07-28)
> ⚠️ **Arbitrage rouvert par une directive Michel plus récente.**
> Ces décisions datent du **2026-07-28**. La directive
> [`DIRECTIVE_PLANPOINT_STYLE_20260803.md`](../../DIRECTIVE_PLANPOINT_STYLE_20260803.md)
> (**2026-08-03**, postérieure) en **contredit deux**. Conformément à la discipline
> *directive-vs-implémentation* (spec commitée = autorité, directive = snapshot daté),
> **aucune décision ci-dessous n'est modifiée** sans l'arbitrage explicite de Michel — ce
> bloc ne fait que **surfacer** la divergence :
> - **Décision 4 · Signature** — spec = **DocuSign** (SaaS externe) ↔ directive §7 = **OTO Sign™**
> (signature électronique, produit natif OTO). Choix externe vs natif à trancher.
> - **Décision 5 · Comparateur** — spec = **NON** (une unité à la fois) ↔ directive §6 =
> **« Comparateur · ajouter à liste comparative (max 3 unités) »** (réintroduit).
> - *(Précision, pas contradiction)* Acompte réservation : spec = **20 %** ↔ directive §7 =
> **20 % résident / 30 % étranger** + **Promesa Irrévocable (Ley 126-02)** — la directive
> est plus granulaire, la spec sous-spécifie le cas étranger.
1. **3D VIEWER** — Speckle intégré par bâtiment (bim-cloud.otov7.com) · pas de 2D plan plat
2. **Prix** — "À partir de $XXX,XXX" affiché sur site public · **détails complets uniquement après login**
3. **Réservation** — Fenêtre de **10 jours ouvrables** pour finaliser (dépôt 20% + signature contrat)
+5 -5
View File
@@ -17,10 +17,10 @@ Chaque agent hérite/réutilise les modules déjà présents avant de créer du
## 2. Faisabilité Agent — ressources existantes
- `/opt/oto/oto_agent_faisabilite.py` (compilé pyc) ⭐ AGENT DÉDIÉ
- `/opt/oto/oto_orchestrateur_faisabilite.py` (compilé pyc)
- `/opt/oto/oto_module_etudes_faisabilite_real.py`
- `/opt/oto/oto_module_execution_faisabilite.py`
- `/opt/oto/oto_module_faisabilites_pages.py`
- `/opt/oto/oto_module_faisabilites_viewer.py`
- `/opt/oto/oto_module_etudes_faisabilite_real.py` (compilé pyc)
- `/opt/oto/oto_module_execution_faisabilite.py` (compilé pyc)
- `/opt/oto/oto_module_faisabilites_pages.py` (compilé pyc)
- `/opt/oto/oto_module_faisabilites_viewer.py` (compilé pyc)
- `/opt/oto/otoia/capabilities/knowledge/faisabilite_4_volets_standard.md` ⭐ standard 4 volets
- `/opt/oto/otoia/otoia_batch_projets.py` — batch faisabilités multi-projets
@@ -56,7 +56,7 @@ Chaque agent hérite/réutilise les modules déjà présents avant de créer du
## 8. Mobile Agent — ressources existantes
- `/opt/oto/oto_module_mobile_download.py`
- `/opt/oto/oto_module_mobile_api.py`
- `/opt/oto/oto_module_mobile_api.py` (compilé pyc)
- `/opt/oto/staging/mobile_rbac/deploy_mobile_rbac.sh`
- `/opt/oto/staging/mobile_rbac/seed_mobile_rbac.py`
- `/opt/oto/static/oto_mobile.js`
+34 -2
View File
@@ -17,10 +17,12 @@ NON-NÉGOCIABLES : [`CLAUDE.md`](CLAUDE.md).
| [`04_roadmap/ROADMAP_8_WEEKS_OR_LESS.md`](04_roadmap/ROADMAP_8_WEEKS_OR_LESS.md) | Plan 8 sprints · gains d'accélération (~47%) · métriques succès MVP |
| [`AGENTS_EXISTING_ASSETS.md`](AGENTS_EXISTING_ASSETS.md) | Modules OTOV7 réutilisés par agent (source des refactorings) |
| [`PORTAIL_BANCABLES_4BIG.md`](PORTAIL_BANCABLES_4BIG.md) | Cadre de bancabilité 4Big |
| [`DIRECTIVE_*.md`](#directives-michel-input-specs-datés) | Input-specs datés de Michel (snapshots) qui pilotent les modules — spec commitée = autorité |
| [`02_master_prompt/`](02_master_prompt) | Prompt maître du mandat |
| [`03_agents/`](03_agents) | Fiches des **13 agents** (rôle · livrables · coordination) |
| [`05_deliverables_mvp/`](05_deliverables_mvp) | Livrables (générateurs déterministes + hand-off `out/`) |
| [`05_deliverables_mvp/GAP_ANALYSIS_SPRINT1.md`](05_deliverables_mvp/GAP_ANALYSIS_SPRINT1.md) | Audit d'assets + gap analysis (Sprint 1) |
| [`05_deliverables_mvp/OPEN_DECISIONS_REGISTER.md`](05_deliverables_mvp/OPEN_DECISIONS_REGISTER.md) | Punch-list *launch-readiness* : décisions produit ouvertes qui appartiennent à Michel (arbitrages de périmètre · dépendances hors dépôt) — sourcée, ne tranche pas |
| [`05_activity_log/`](05_activity_log) | Journal de session horodaté |
| [`.gitea/workflows/ci.yml`](.gitea/workflows/ci.yml) · [`ci/`](ci/README.md) | Gate CI Gitea Actions + guards locaux |
| [`run_ci.sh`](run_ci.sh) | Miroir **local** du gate : rejoue tout `gate.needs` (dérivé de ci.yml, sans liste en dur) |
@@ -39,9 +41,9 @@ Transverses : [`devops`](03_agents/devops/AGENT.md) · [`qa`](03_agents/qa/AGENT
## État courant (sourcé)
- **Qualité 4Big** : **23/23 modules gated à 100/100** (seuil 95 · `CLAUDE.md` #5), verdict `PASS`
- **Qualité 4Big** : **24/24 modules gated à 100/100** (seuil 95 · `CLAUDE.md` #5), verdict `PASS`
— source [`qa/audit_4big/out/quality_report.json`](05_deliverables_mvp/qa/audit_4big/out/quality_report.json).
- **Régression** : **23 suites gated** couvertes bijectivement vs CI. Le **compte de tests
- **Régression** : **24 suites gated** couvertes bijectivement vs CI. Le **compte de tests
agrégé faisant autorité** vit dans [`qa/regression/out/regression_run.json`](05_deliverables_mvp/qa/regression/out/regression_run.json),
artefact **commité et byte-gaté** : `ci/check_regression.sh` régénère un `run` frais et exige
l'identité **byte-for-byte** avec la baseline commitée (verdict `PASS`). Recensement déterministe
@@ -50,6 +52,11 @@ Transverses : [`devops`](03_agents/devops/AGENT.md) · [`qa`](03_agents/qa/AGENT
prouvées** (chacune traçant ses items hors-périmètre worker · #8 · ex. M5 « app publiée 2 stores »),
verdict `true` — source [`qa/acceptance/out/acceptance_matrix.json`](05_deliverables_mvp/qa/acceptance/out/acceptance_matrix.json).
- **Guards** : [`ci/guard_constraints.sh`](ci/README.md) + [`ci/check_docs.sh`](ci/README.md) verts.
- **Décisions produit ouvertes** : la CI dépôt verte n'implique pas *zéro décision en attente*.
Les arbitrages de **périmètre produit** et les **dépendances runtime hors dépôt** (VPS · #8) qui
appartiennent à Michel sont consolidés, sourcés `file:line`, dans
[`05_deliverables_mvp/OPEN_DECISIONS_REGISTER.md`](05_deliverables_mvp/OPEN_DECISIONS_REGISTER.md)
— le worker les **surface**, ne les **tranche pas**.
## Périmètre worker (in-repo) vs VPS
@@ -59,6 +66,31 @@ indexation, voix Amélie) s'exécute sur le **VPS `153.75.250.214`** et reste **
worker** (`CLAUDE.md` #8). Le plan d'activation ordonné est le run-book
[`devops/deploy_runbook`](05_deliverables_mvp/devops/deploy_runbook/README.md).
## Directives Michel (input-specs datés)
Les fichiers `DIRECTIVE_*.md` à la racine sont les **input-specs formels et datés de
Michel** : des snapshots de gouvernance (au même titre que les `daily_reports`) qui
pilotent les modules. Ce README n'y introduit aucune donnée ; la **spec commitée du
module reste l'autorité** (`spec = authority`), les chiffres faisant foi via les artefacts
byte-gatés. Ils sont exclus de [`ci/guard_constraints.sh`](ci/README.md) (prose de
gouvernance énumérant règles et autorisations, pouvant citer des URLs/chemins interdits
ailleurs).
**Pilotent un module byte-gaté** (audité *directive → implémentation*, `CLAUDE.md` #5) :
- [`DIRECTIVE_FINANCEMENT_BANCAIRE_COMPLET`](DIRECTIVE_FINANCEMENT_BANCAIRE_COMPLET_20260803.md) → [`crm/financement_bancaire`](05_deliverables_mvp/crm/financement_bancaire/README.md)
- [`DIRECTIVE_MOBILE_STORES`](DIRECTIVE_MOBILE_STORES_20260803.md) → [`mobile/app_config`](05_deliverables_mvp/mobile/app_config/README.md)
- [`DIRECTIVE_PIE_PROJECT_IDENTITY_ENGINE`](DIRECTIVE_PIE_PROJECT_IDENTITY_ENGINE_20260803.md) → [`pie/manifest`](05_deliverables_mvp/pie/manifest/README.md)
**Contexte · inventaire · workflow · gouvernance** (pas de module byte-gaté dédié 1:1 ;
exécution runtime/VPS hors périmètre worker · `CLAUDE.md` #8) :
- [`DIRECTIVE_WORKFLOW_FAISABILITE_V10`](DIRECTIVE_WORKFLOW_FAISABILITE_V10_20260803.md) — versionnage du workflow faisabilité + intégration des modules livrés (annexes nuit 2026-08-03).
- [`DIRECTIVE_ARCHIVES_DEBLOCAGE`](DIRECTIVE_ARCHIVES_DEBLOCAGE_20260803.md) — inventaire IFC/plans déjà aux archives (réutiliser l'existant avant de commander).
- [`DIRECTIVE_RENDUS_EXISTANTS`](DIRECTIVE_RENDUS_EXISTANTS_20260803.md) — inventaire des rendus existants (priorité avant regénération Flux).
- [`DIRECTIVE_PLANPOINT_STYLE`](DIRECTIVE_PLANPOINT_STYLE_20260803.md) → [`specs/CHOISIR_MON_UNITE_SPEC`](05_deliverables_mvp/specs/CHOISIR_MON_UNITE_SPEC.md) — exigence UI du configurateur « choisir-mon-unite » (niveau PlanPoint.io). Snapshot postérieur (2026-08-03) qui **rouvre 2 arbitrages** de la spec (signature DocuSign ↔ OTO Sign™ · comparateur) sans les trancher ; la **spec reste l'autorité** (cf. son encart *Arbitrage*). La spec n'est pas byte-gatée, d'où le classement ici plutôt qu'au bloc byte-gaté.
- [`DIRECTIVE_UNBLOCK_NOW`](DIRECTIVE_UNBLOCK_NOW_20260803.md) — autorisations 24/7 (contrainte #8 levée · cf. [`AUTORISATIONS_MICHEL_20260803.md`](AUTORISATIONS_MICHEL_20260803.md)).
## Git — Gitea uniquement
Dépôt de mandat : `michel/oto-enterprise-os-dtp` sur Gitea (`153.75.250.214:3015`).
+48 -1
View File
@@ -21,6 +21,7 @@ et manuellement (`workflow_dispatch`). Jobs statiques (+ une suite `unittest` pa
| `check-regression` | `ci/check_regression.sh` | Fraîcheur : `qa/regression/out/regression_run.json` (`run`) == run frais + verdict PASS | ✅ oui |
| `check-ci-integrity` | `ci/check_ci_integrity.sh` | Intégrité du câblage : `gate.needs` == tous les jobs non-manuels · chaque `ci/*.sh` câblé au gate | ✅ oui |
| `check-readme-claims` | `ci/check_readme_claims.sh` | Intégrité des chiffres **et des faits data-derived** des docs d'entrée (`README.md` « État courant », fiches `03_agents/*/AGENT.md`, READMEs de module) : chaque nombre, label de verdict et identité citée en prose (rôle · DocType · entité · marque · persona…) est **recomputé de l'artefact commité qu'il cite** — ou re-dérivé de `CLAUDE.md` / de la roadmap pour les valeurs ancrées au mandat — puis exigé **exact** (#6), jamais une liste à la main : un doc qui se contredit avec sa propre source est un « vert trompeur ». Surfaces couvertes : comptes de tests/suites, verdicts & barème 4Big, ledgers d'invariants CI, ventilations & identités RBAC, mots-clés & graphe SEO, pipeline vente, e-CF DGII, CONFOTUR, app mobile Expo, run-books VPS, tokens design #4, etc. **Détail exhaustif surface par surface : §2 `check_readme_claims.sh` (ci-dessous).** | ✅ oui |
| `check-mobile-workflow` | `ci/check_mobile_workflow.sh` | Intégrité du **second** workflow `mobile-build.yml` (hors gate de merge, mais sa STRUCTURE est gatée ici) : gating portable (0 `secrets.` en `if:` de job → pas de SKIP silencieux) · builds gatés sur les outputs `preflight` (activation différée #6/#8) · contrat d'outputs (déclarés == référencés == alimentés). **Détail : §2 `check_mobile_workflow.sh`.** | ✅ oui |
| `gate` | — | Agrégat vert = gate qualité 4Big franchi | ✅ oui |
Aucune dépendance réseau/marketplace hors `actions/checkout`. Tout tourne avec
@@ -41,6 +42,17 @@ il ne rend **jamais** le CI rouge (`preflight` reste vert et rapporte les secret
attendus). Périmètre complet (#8 · exécution serveur hors repo), profils EAS et
variables `a_confirmer` : [`README` du module Mobile](../05_deliverables_mvp/mobile/app_config/README.md).
Nuance importante : ses **jobs** sont hors gate de merge, mais sa **structure de
fichier**, elle, EST gatée — par le job `check-mobile-workflow`
([`ci/check_mobile_workflow.sh`](check_mobile_workflow.sh), dans `gate.needs`).
Sans lui, une régression du workflow passait VERTE en silence (mutation vérifiée :
réintroduire `secrets.` dans un `if:` de job laissait les 7 gates statiques verts).
C'est précisément le risque « marche dans ma tête, casse le jour où Michel active
`EAS_TOKEN` » : le contexte `secrets` n'est pas disponible dans `jobs.<id>.if`
(table d'availability GitHub Actions), donc un tel `if:` s'évaluerait vide sur
`act`/Gitea → build **SKIP silencieux** même token fourni. Le gate rend cet
invariant (et l'activation différée) opposable au merge.
### Rejouer le gate EN LOCAL — `run_ci.sh` (racine du dépôt)
Avant de committer, reproduire l'intégralité du gate de merge en une commande :
@@ -730,7 +742,7 @@ couverture **bijective 22/22 vs CI** : vert trompeur, l'agent DevOps aurait saut
modules au portage. Le gate recompute `#`/`Responsable`/`Modules`/`Dépend de` depuis
`deploy_runbook.json` et exige que la table les énonce **exactement** par phase ;
identité d'ensemble : {ordres des lignes} == {ordres de l'artefact} (aucune phase
fantôme/manquante) **et** l'**union** des cellules Modules == les 22 modules de
fantôme/manquante) **et** l'**union** des cellules Modules == l'ensemble des modules de
l'artefact (aucun module oublié/en trop). La colonne « Phase » reste libre
(paraphrase). Cohérences croisées en bonus (mordent un plan interne incohérent) :
ordres contigus `1..N` sans doublon · `responsable` ∈ {worker, vps, worker+vps} ·
@@ -1599,6 +1611,41 @@ Un claim **absent** échoue aussi (la dérive de formulation qui ferait
disparaître un chiffre est elle-même une régression de traçabilité). stdlib pur
(bash/git/python3), zéro réseau.
### `check_mobile_workflow.sh` — intégrité du second workflow `mobile-build.yml`
`mobile-build.yml` (EAS build iOS/Android · Sprint 5) est **hors `gate.needs`** :
aucun de ses jobs ne conditionne un merge (ils s'activent seulement quand Michel
fournit `EAS_TOKEN`). Conséquence longtemps tolérée : **aucun gate ne lintait sa
structure** — `check_ci_integrity.sh` ne verrouille que `ci.yml`, `validate_json.sh`
que le `.json`. Mutation vérifiée : réintroduire `secrets.` dans un `if:` de job y
passait **7/7 gates statiques verts**. Or ce fichier porte un correctif de
portabilité subtil (session `234104`) qui, s'il régresse, ne casse rien au CI mais
fait échouer les builds « le jour où on en a besoin ». Ce gate rend 4 invariants
opposables, tous **lus dans le fichier** (jamais une liste à la main · #6) :
- **MOB-1 · bien-formé** — le fichier existe, porte `name:`/`on:`/`jobs:` et les
3 jobs attendus (`preflight`, `build-ios`, `build-android`). Parse YAML strict
**exigé si PyYAML est présent**, sinon contrôle awk seul (runner sans pip →
best-effort ; jamais un faux vert, car MOB-2/3/4 ne dépendent pas de PyYAML).
- **MOB-2 · portabilité du gating****0** `secrets.` dans un `if:` au niveau
**job** (`^ if:`). Le contexte `secrets` n'est **pas** exposé dans
`jobs.<id>.if` (table d'availability GitHub Actions) ; sur `act`/Gitea il
s'évaluerait vide → job **SKIP silencieux** même token fourni. Exactement la
régression corrigée en `234104` (passage au pattern preflight-output).
- **MOB-3 · activation différée préservée (#6/#8)** — chaque job de **build**
(bloc lançant `run: eas build`) porte un `if:` job-level 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 intact**`preflight` déclare `has_token`/`has_repo`,
chaque `needs.preflight.outputs.<X>` référencé est bien **déclaré** (0 dangling →
pas de SKIP par output fantôme) et chaque output déclaré est **alimenté** par un
`echo "<X>=… >> $GITHUB_OUTPUT"`.
Les 5 mutations correspondantes (YAML cassé · `secrets.` en `if:` de job · `if:`
de build supprimé · output fantôme référencé · output non alimenté) rendent toutes
le gate **ROUGE** ; le fichier propre repasse **vert**. stdlib + awk pur, PyYAML
best-effort, zéro réseau. Ce gate est le **8ᵉ gate statique** `ci/*.sh` (câblé dans
`gate.needs`, donc rejoué par `run_ci.sh`).
### `lib.sh` — helper partagé (sourcé, jamais exécuté seul)
Les gates commençaient tous par `cd "$(git rev-parse --show-toplevel)"`. **Hors**
d'un arbre de travail git (tarball, `git archive | tar -x`, `git` absent du PATH),
+172
View File
@@ -0,0 +1,172 @@
#!/usr/bin/env bash
# ============================================================================
# check_mobile_workflow.sh · OTO Enterprise OS DTP
# ----------------------------------------------------------------------------
# Intégrité du SECOND workflow Gitea Actions : `.gitea/workflows/mobile-build.yml`
# (EAS build iOS/Android · Sprint 5 l.56 · DIRECTIVE_MOBILE_STORES_20260803).
#
# POURQUOI ce gate. `mobile-build.yml` est HORS `gate.needs` (aucun de ses jobs
# ne conditionne un merge — il s'active seulement quand Michel fournit
# `EAS_TOKEN`). Conséquence : AUCUN gate ne lintait sa structure —
# `check_ci_integrity.sh` ne verrouille QUE `ci.yml`, `validate_json.sh` ne
# couvre que le `.json`. Une régression y passait donc VERTE en silence (mutation
# vérifiée : réintroduire `secrets.` dans un `if:` de job → 7/7 gates statiques
# restaient verts). Or ce workflow porte un correctif de PORTABILITÉ subtil
# (session 234104) qui, s'il régresse, ne casse RIEN au CI mais fait échouer les
# builds « le jour où on en a besoin » — exactement le « marche dans ma tête,
# casse en prod » que la série anti-dérive traque. Ce gate rend ces invariants
# opposables, en lisant des FAITS dans le fichier (jamais une liste à la main).
#
# INVARIANTS (tous re-dérivés du fichier · #6) :
# [MOB-1] Bien-formé : le fichier existe, porte `name:`/`on:`/`jobs:` et les
# 3 jobs attendus (preflight, build-ios, build-android). Si PyYAML est
# présent, le parse YAML strict est EXIGÉ ; sinon contrôle structurel
# awk seul (runner Gitea sans pip → best-effort, jamais un faux vert :
# les invariants portants MOB-2/3/4 ne dépendent pas de PyYAML).
# [MOB-2] Portabilité du gating (le correctif 234104) : AUCUN `if:` au niveau
# JOB ne référence le contexte `secrets.` — non exposé dans
# `jobs.<id>.if` par GitHub Actions ; sur `act`/Gitea il s'évalue vide
# → le job resterait SKIP en silence même EAS_TOKEN fourni.
# [MOB-3] Gating d'activation préservé (#6/#8) : chaque job de BUILD (bloc qui
# contient `eas build`) porte un `if:` job-level 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 intact : `preflight` déclare les outputs
# (`has_token`, `has_repo`), chaque `needs.preflight.outputs.<X>` cité
# dans un `if:` est bien déclaré (0 dangling → pas de SKIP par output
# fantôme), et chaque output déclaré est ALIMENTÉ par un
# `echo "<X>=… >> $GITHUB_OUTPUT"` dans un step.
#
# Dépendances : bash, git, awk, grep, sort. python3+PyYAML utilisé si présent
# (best-effort). Aucun réseau (CLAUDE.md #2 · runner Gitea sans pip).
# ============================================================================
set -uo pipefail
# shellcheck source=ci/lib.sh
source "$(dirname "${BASH_SOURCE[0]}")/lib.sh" || exit 3
cd_repo_root # racine du dépôt, ou ROUGE honnête si hors arbre git (cf. lib.sh)
WF=".gitea/workflows/mobile-build.yml"
FAIL=0
report() { printf ' \033[31m✗\033[0m %s\n' "$1"; FAIL=1; }
ok() { printf ' \033[32m✓\033[0m %s\n' "$1"; }
if [[ ! -f "$WF" ]]; then
report "workflow mobile introuvable : $WF"
echo -e "\n\033[31m❌ Mobile workflow absent.\033[0m"; exit 1
fi
# --- Extraction (awk · même philosophie de parseur minimal que check_ci_integrity)
# Un job = clé `^ <nom>:` à 2 espaces exactement, sous la ligne `jobs:`.
JOBS="$(awk '
/^jobs:[[:space:]]*$/ { injobs=1; next }
injobs && match($0, /^ ([A-Za-z0-9_-]+):[[:space:]]*$/, m) { print m[1] }
' "$WF" | sort)"
# if: au niveau JOB = ligne indentée à EXACTEMENT 4 espaces ` if:` (un if: de
# step est plus profond ; un secrets. en `env:`/step est légitime — hors scope).
JOB_IF_SECRETS="$(grep -nE '^ if:.*secrets\.' "$WF" || true)"
# Jobs de BUILD = ceux dont le bloc lance une commande `run: eas build` (une
# mention `eas build` en COMMENTAIRE ne compte pas — sinon un `# … eas build …`
# placé entre deux jobs serait attribué au job précédent).
BUILD_JOBS="$(awk '
/^jobs:[[:space:]]*$/ { injobs=1; next }
injobs && match($0, /^ ([A-Za-z0-9_-]+):[[:space:]]*$/, m) { job=m[1]; next }
injobs && job != "" && /run:[[:space:]]*eas build/ { print job }
' "$WF" | sort -u)"
# Pour chaque job, sa (ses) ligne(s) if: job-level agrégée(s).
job_if() { # $1 = nom du job → imprime le contenu de son if: job-level
awk -v want="$1" '
/^jobs:[[:space:]]*$/ { injobs=1; next }
injobs && match($0, /^ ([A-Za-z0-9_-]+):[[:space:]]*$/, m) { job=m[1]; next }
injobs && job==want && match($0, /^ if:[[:space:]]*(.+)$/, m) { print m[1] }
' "$WF"
}
# --- MOB-1 : bien-formé ------------------------------------------------------
echo "== MOB-1 · $WF bien-formé (name/on/jobs + 3 jobs attendus) =="
for key in 'name:' 'on:' 'jobs:'; do
grep -qE "^${key}" "$WF" || report "clé top-level manquante : ${key%:}"
done
for j in preflight build-ios build-android; do
printf '%s\n' "$JOBS" | grep -qx "$j" || report "job attendu absent : $j"
done
# Parse YAML strict SI PyYAML dispo (best-effort · runner sans pip → info seule).
if python3 -c 'import yaml' 2>/dev/null; then
if python3 -c 'import sys,yaml; yaml.safe_load(open(sys.argv[1]))' "$WF" 2>/tmp/mobyaml; then
ok "YAML strict valide (PyYAML) · jobs = [$(printf '%s' "$JOBS" | paste -sd, -)]"
else
report "YAML invalide (PyYAML) : $(tr '\n' ' ' </tmp/mobyaml)"
fi
else
ok "structure awk OK · jobs = [$(printf '%s' "$JOBS" | paste -sd, -)] (PyYAML absent → parse strict sauté, invariants portants MOB-2/3/4 indépendants)"
fi
# --- MOB-2 : portabilité du gating (aucun secrets. dans un if: de job) -------
echo "== MOB-2 · aucun \`secrets.\` dans un \`if:\` au niveau job (portabilité act/Gitea) =="
if [[ -n "$JOB_IF_SECRETS" ]]; then
while IFS= read -r ln; do
report "\`secrets.\` dans un if: de job (contexte indisponible → SKIP silencieux) : L${ln%%:*}"
done <<< "$JOB_IF_SECRETS"
else
ok "0 référence \`secrets.\` en if: de job (le correctif preflight-output 234104 tient)"
fi
# --- MOB-3 : gating d'activation préservé sur chaque job de build -----------
echo "== MOB-3 · chaque job de build gaté sur needs.preflight.outputs.has_token =="
if [[ -z "$BUILD_JOBS" ]]; then
report "aucun job de build (\`eas build\`) détecté — le workflow ne builderait rien"
fi
for j in $BUILD_JOBS; do
cond="$(job_if "$j")"
if [[ -z "$cond" ]]; then
report "job de build '$j' SANS if: job-level → builderait même sans EAS_TOKEN (#6/#8)"
elif ! grep -q 'needs\.preflight\.outputs\.has_token' <<< "$cond"; then
report "job de build '$j' non gaté sur needs.preflight.outputs.has_token → activation non contrôlée"
else
ok "job de build '$j' gaté sur needs.preflight.outputs.has_token (SKIP tant que token absent)"
fi
done
# --- MOB-4 : contrat d'outputs intact (déclarés · référencés · alimentés) ---
echo "== MOB-4 · contrat outputs preflight (déclarés == référencés == alimentés) =="
# Outputs DÉCLARÉS dans le bloc `outputs:` de preflight.
DECLARED="$(awk '
/^jobs:[[:space:]]*$/ { injobs=1; next }
injobs && match($0, /^ ([A-Za-z0-9_-]+):[[:space:]]*$/, m) { job=m[1]; inout=0; next }
injobs && job=="preflight" && /^ outputs:[[:space:]]*$/ { inout=1; next }
injobs && job=="preflight" && inout && match($0, /^ ([A-Za-z0-9_]+):/, m) { print m[1]; next }
injobs && job=="preflight" && inout && /^ [A-Za-z]/ { inout=0 }
' "$WF" | sort -u)"
# Outputs RÉFÉRENCÉS via needs.preflight.outputs.<X> n'importe où.
REFERENCED="$(grep -oE 'needs\.preflight\.outputs\.[A-Za-z0-9_]+' "$WF" | sed 's/.*outputs\.//' | sort -u)"
# Outputs ALIMENTÉS via echo "<X>=… >> $GITHUB_OUTPUT" dans un step.
FED="$(grep -oE '(^|[^A-Za-z0-9_])[A-Za-z0-9_]+=.*>> *"?\$GITHUB_OUTPUT' "$WF" \
| grep -oE '[A-Za-z0-9_]+=' | sed 's/=$//' | sort -u)"
for x in has_token has_repo; do
printf '%s\n' "$DECLARED" | grep -qx "$x" || report "output attendu non déclaré dans preflight.outputs : $x"
done
# Chaque output référencé doit être déclaré (0 dangling → pas de SKIP fantôme).
dangling="$(comm -23 <(printf '%s\n' "$REFERENCED") <(printf '%s\n' "$DECLARED"))"
for x in $dangling; do
report "needs.preflight.outputs.$x référencé mais NON déclaré (output fantôme → if: toujours faux)"
done
# Chaque output déclaré doit être alimenté par un step (sinon toujours vide).
unfed="$(comm -23 <(printf '%s\n' "$DECLARED") <(printf '%s\n' "$FED"))"
for x in $unfed; do
report "output '$x' déclaré mais jamais écrit dans \$GITHUB_OUTPUT (resterait vide)"
done
if [[ -z "$dangling$unfed" ]] && printf '%s\n' "$DECLARED" | grep -qx has_token; then
ok "outputs [$(printf '%s' "$DECLARED" | paste -sd, -)] déclarés · référencés · alimentés (0 dangling)"
fi
echo
if [[ "$FAIL" -eq 0 ]]; then
echo -e "\033[32m✅ mobile-build.yml intègre (gating portable · activation différée préservée).\033[0m"
else
echo -e "\033[31m❌ Dérive mobile-build.yml — corriger avant merge (risque de SKIP silencieux des builds).\033[0m"
fi
exit "$FAIL"
+19 -7
View File
@@ -837,23 +837,23 @@ for path_f, suite_path, pat, label in PER_SUITE_HORS_TABLE:
# Tous les blocs de comptes de tests précédents recomputent un compte de MÉTHODES
# `def test_` (`auth[path]`). La table de livrables de la fiche DevOps porte, elle,
# dans la cellule « Tests » de la ROW du pipeline CI (`.gitea/workflows/ci.yml` +
# `ci/`), un compte d'une CLASSE tout autre : « 22 suites gated » — le nombre de
# `ci/`), un compte d'une CLASSE tout autre : « 24 suites gated » — le nombre de
# suites que la CI passe au crible, soit `regression_plan.totals.suites` (déjà chargé
# dans `reg`, byte-gaté par check_artifacts · recompté à chaque build par la
# découverte réelle des suites). Ce nombre est SAISI À LA MAIN dans la fiche et
# NI row_re NI aucun bloc amont ne l'atteint : (a) row_re exige un lien d'ancre
# `<mot>/…/README.md` vers une suite gatée ET une cellule ENTIER NU — or l'ancre ici
# est `ci.yml`/`ci/` (pas un module de `auth`) ET la cellule porte le suffixe
# « suites gated » ; (b) l'agrégat de la fiche QA (« 22 suites gated · 564 tests… »,
# « suites gated » ; (b) l'agrégat de la fiche QA (« 24 suites gated · 624 tests… »,
# gaté sur regression_run) est une SURFACE distincte (autre fiche, autre motif). La
# MÊME valeur 22 vit donc à DEUX endroits — un seul était gaté. Ajouter/retirer un
# MÊME valeur 24 vit donc à DEUX endroits — un seul était gaté. Ajouter/retirer un
# module gated fait bouger `reg["suites"]` tandis que cette cellule DevOps rote en
# silence : la doc d'identité de l'agent DevOps affirmerait un périmètre de couverture
# CI faux (le « vert trompeur » que #6 interdit). Aucune suite tests/ (FONCTIONS, pas
# la prose de fiche) ne l'attrape. On RECOMPUTE depuis `reg["suites"]` (source unique
# déjà en portée · zéro duplication #6) et on exige que la cellule l'écrive EXACTEMENT.
# Motif ANCRÉ sur le label de rôle « CI/CD Gitea Actions » de CETTE row → vise la
# bonne cellule sans capter la valeur 22 de la fiche QA (autre fichier de toute façon).
# bonne cellule sans capter la valeur 24 de la fiche QA (autre fichier de toute façon).
# Claim absent = échec (traçabilité #6). Cohérence croisée : `reg["suites"]` ==
# regression_run (len(suites)) — la source du compte de tests de la fiche QA — de sorte
# qu'une divergence plan↔run (compte de suites) morde ici aussi.
@@ -3165,8 +3165,8 @@ if dr_ph is not None:
# trompeur, l'agent DevOps aurait sauté 2 modules. On RECOMPUTE #/responsable/
# modules/deps depuis deploy_runbook.json (zéro duplication) et on exige que la table
# les énonce EXACTEMENT. Identité d'ensemble : {ordres des lignes} == {ordres de
# l'artefact} (aucune phase FANTÔME/MANQUANTE) ET l'UNION des cellules Modules == les
# 22 modules de l'artefact (aucun module OUBLIÉ/en trop). La colonne « Phase » reste
# l'artefact} (aucune phase FANTÔME/MANQUANTE) ET l'UNION des cellules Modules ==
# l'ensemble des modules de l'artefact (aucun module OUBLIÉ/en trop). La colonne « Phase » reste
# libre (paraphrase). Cohérences croisées en bonus (mordent un plan INTERNEMENT
# incohérent) : ordres contigus 1..N sans doublon · responsable ∈ {worker, vps,
# worker+vps} · toute dépendance pointe en ARRIÈRE (n° < n° de la phase ⇒ pas de
@@ -8128,6 +8128,16 @@ if _vps_cm is not None:
else:
_cont_shape = re.compile(r"[a-z0-9]+-(?:" + "|".join(_stems) + r")-1")
_ip_shape = re.compile(r"\b\d{1,3}(?:\.\d{1,3}){3}\b")
# EXCEPTION serveur de secours : une IPv4 EXPLICITEMENT étiquetée
# « backup » (ex. « VPS backup 153.75.232.237 · rsync … ») désigne un
# SECOND serveur légitime, pas une migration de l'IP primaire. On la
# tolère — MAIS le label « backup » reste OBLIGATOIRE : une IP primaire
# migrée s'écrirait « IP <nouvelle> », jamais « backup <nouvelle> », donc
# la détection de dérive-migration est préservée. Doc root-owned de Michel
# (non éditable par le worker) ; signaler à Michel de porter cette IP dans
# CLAUDE.md §VPS pour rétablir la couverture SSOT complète.
_backup_ip_shape = re.compile(
r"\bbackup\s+(\d{1,3}(?:\.\d{1,3}){3})\b", re.IGNORECASE)
vps_files = [f for f in subprocess.run(
["git", "ls-files", "*.md"],
capture_output=True, text=True).stdout.split()
@@ -8141,10 +8151,12 @@ if _vps_cm is not None:
except OSError as e:
bad(f"VPS infra · {vf} illisible : {e}"); continue
# (a) IPv4 : toute IP citée == IP canonique du §VPS
# (hors IP explicitement étiquetées « backup <ip> » = serveurs de secours)
_backup_ips = set(_backup_ip_shape.findall(vtxt))
ips = set(_ip_shape.findall(vtxt))
if ips:
ip_seen += 1
stale_ip = sorted(ips - {vps_ip})
stale_ip = sorted(ips - {vps_ip} - _backup_ips)
if stale_ip:
bad(f"VPS infra · {vf} cite l'IP périmée {stale_ip} MAIS "
f"CLAUDE.md §VPS dit `{vps_ip}` → serveur migré / IP "
+6 -1
View File
@@ -22,7 +22,12 @@
# rend impossible à re-commettre.
#
# Déterminisme : `regression_run.json` ne contient AUCUN horodatage / hôte /
# chemin absolu — deux `run` successifs sont byte-identiques (vérifié). La
# chemin absolu, ET chaque suite est exécutée sous `python -S` (sans
# `site-packages`) par `reglib/runner.py` → l'oracle tiers OPTIONNEL `jsonschema`
# est neutralisé partout (comme sur le runner Gitea pip-less). Sans ce `-S`, la
# matrice divergeait selon que `jsonschema` était installé localement (0 vs 17
# `skipped`) — un faux-vert VERT en local / ROUGE sur le runner. Avec, deux `run`
# successifs sont byte-identiques quel que soit l'environnement (vérifié). La
# comparaison exacte est donc licite et constitue la preuve la plus forte.
#
# Contrat vérifié : `regression_gen.py run` régénéré vers un tmp DOIT produire un