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 - name: Contrôle des chiffres du README
run: bash ci/check_readme_claims.sh 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. # Publiciste (Sprint 2) : parser faisabilité → projets_master.json + generator.
# Tests unitaires stdlib pur (unittest) → aucune installation pip requise sur # Tests unitaires stdlib pur (unittest) → aucune installation pip requise sur
@@ -566,7 +582,7 @@ jobs:
gate: gate:
name: Gate qualité (agrégat) name: Gate qualité (agrégat)
runs-on: ubuntu-latest 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: steps:
- name: Résultat - name: Résultat
run: echo "✅ Gate qualité 4Big franchi — tous les checks verts." run: echo "✅ Gate qualité 4Big franchi — tous les checks verts."
+22 -3
View File
@@ -70,19 +70,38 @@ jobs:
preflight: preflight:
name: Préflight config EAS + état secrets name: Préflight config EAS + état secrets
runs-on: ubuntu-latest 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: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Config EAS versionnée présente (eas_build.json) - 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" 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é) - name: État des secrets requis (aucun secret n'est loggé)
id: secrets
run: | run: |
echo "Profil sélectionné : $EAS_PROFILE" echo "Profil sélectionné : $EAS_PROFILE"
if [ -n "${{ secrets.EAS_TOKEN }}" ]; then if [ -n "${{ secrets.EAS_TOKEN }}" ]; then
echo "EAS_TOKEN : présent → les jobs de build vont s'exécuter." echo "EAS_TOKEN : présent → les jobs de build vont s'exécuter."
echo "has_token=true" >> "$GITHUB_OUTPUT"
else else
echo "EAS_TOKEN : ABSENT → jobs de build SKIP (attente Michel · expo.dev)." 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 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). # Build iOS · SKIP tant que EAS_TOKEN absent (activation Michel · #6/#8).
@@ -92,7 +111,7 @@ jobs:
name: EAS build iOS (App Store #32) name: EAS build iOS (App Store #32)
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: preflight 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: env:
EXPO_TOKEN: ${{ secrets.EAS_TOKEN }} EXPO_TOKEN: ${{ secrets.EAS_TOKEN }}
steps: steps:
@@ -117,7 +136,7 @@ jobs:
name: EAS build Android (Play Store) name: EAS build Android (Play Store)
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: preflight 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: env:
EXPO_TOKEN: ${{ secrets.EAS_TOKEN }} EXPO_TOKEN: ${{ secrets.EAS_TOKEN }}
steps: 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 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) ; 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 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` ## 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 **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 | | 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) | | [`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) | | [`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** 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 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 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). `qa/regression/out/regression_run.json` — jamais compté à la main · #6).
## Hand-off reçus (à appliquer sur le VPS, dans l'ordre) ## 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 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). 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 ## Sources canoniques
> **Chemins VPS runtime — hors périmètre worker (#8), non commités in-repo** (préfixe > **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, > 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 - **Publiciste Agent** : consomme la faisabilité pour maintenir vente.otov7.com
- **ERPNext Backend** : stocke DocType "Faisabilité" avec version + score qualité - **ERPNext Backend** : stocke DocType "Faisabilité" avec version + score qualité
- **QA Agent** : valide score 4Big ≥ 95/100 avant publication - **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) ## Livrable Sprint (mandat 8 semaines)
- **S1** : Template canonique v1.0 finalisé + agent scaffold - **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 **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, 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 « 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 livrable in-repo de l'agent est le **générateur de config app** `mobile/app_config`
et le **rôle RBAC** qui les gouverne, validés par auto-vérification côté serveur (§ dédiée ci-dessous · la config **versionnable** de l'app, pas le binaire), la
(HTTP + builds EAS). **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) ## 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 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 | | 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 > **volontairement pas** rattaché au Workspace `OTO Ventes` ni à la liste
> `roles_allowed` du chat OTOIA — ces surfaces couvrent les portails > `roles_allowed` du chat OTOIA — ces surfaces couvrent les portails
> construction/vente. Ne pas revendiquer ici un contact workspace/chat qui > 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`, > 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 > `qa-acceptance-tests`, dans le `gate`). Le rôle est **spécifié en-repo** ; son
> **exécution** (EAS/stores/OTA) reste hors repo (#8). > **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`) ## Modules OTOV7 réels refactorés (source : `AGENTS_EXISTING_ASSETS.md §8`)
| Module VPS (hors-repo) | Fonction | État roadmap | | 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/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/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`) | | `/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. **conformité** normative des hand-off.
## Verdict agrégé courant (source `qa/regression/out/regression_run.json`) ## 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`, Ce compte est **découvert automatiquement depuis le CI** (`q4lib.registry.parse_ci`,
réutilisé sans duplication) — jamais compté à la main. 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`) ## 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 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 # 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 ## 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 **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é). 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 : `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. 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 # 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 financement).** Matérialise la
[`DIRECTIVE_FINANCEMENT_BANCAIRE_COMPLET_20260803.md`](../../../DIRECTIVE_FINANCEMENT_BANCAIRE_COMPLET_20260803.md) [`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 (Michel, 2026-08-03) : un module **complet** de financement bancaire pour un vrai
parcours hypothécaire dominicain, à la place du module « trop simpliste » 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 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 > 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 > et les artefacts en-repo ; le backend `/api/hypotheque/*` et le frontend
@@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Générateur du module Financement Bancaire — parcours hypothécaire RD. """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). financement) + DIRECTIVE_FINANCEMENT_BANCAIRE_COMPLET_20260803.md (Michel).
Transforme le contrat `financement_spec.json` en artefacts prêts à consommer 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. charger le contrat, assembler le bundle, valider (schéma + invariants), écrire.
Anti-invention (#6) : aucune valeur (taux d'apport, documents, autorisations, 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). 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. 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 **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 `EAS_TOKEN` Michel + `MOBILE_NATIVE_REPO`), déploiement production VPS → agents
Mobile/DevOps. 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` | — | | 1 | vps | Prérequis (modules custom OTOV7 + spec RBAC) | `rbac` | — |
| 2 | vps | DocTypes custom | `crm/dossier_vente`, `legal/confotur` | 1 | | 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 | | 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 | | 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 | | 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`, **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) | | Page projet (site) | [`publiciste`](../../publiciste/README.md) |
| Section app mobile | [`mobile/app_config`](../../mobile/app_config/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 slogan, calendrier, trackers chantier) sont marqués `a_construire` (P1-P4 de la
directive) — aucun sur-engagement. directive) — aucun sur-engagement.
@@ -280,7 +280,7 @@ def _validate_or_die(bundle: dict) -> None:
sys.exit(1) sys.exit(1)
def cmd_build(args) -> None: def cmd_build(args) -> int:
bundle = _build_bundle() bundle = _build_bundle()
_validate_or_die(bundle) _validate_or_die(bundle)
out_dir = args.out or _DEFAULT_OUT out_dir = args.out or _DEFAULT_OUT
@@ -297,17 +297,19 @@ def cmd_build(args) -> None:
f"hors-périmètre sourcés.") f"hors-périmètre sourcés.")
print(f"{os.path.join(out_dir, 'acceptance_matrix.json')}") print(f"{os.path.join(out_dir, 'acceptance_matrix.json')}")
print(f"{os.path.join(out_dir, 'MANIFEST.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() bundle = _build_bundle()
_validate_or_die(bundle) _validate_or_die(bundle)
cov = bundle["manifest"]["coverage"] cov = bundle["manifest"]["coverage"]
print(f"✅ Matrice valide — bijective={cov['bijective']} · " print(f"✅ Matrice valide — bijective={cov['bijective']} · "
f"partition_ok={cov['partition_ok']} · verdict={bundle['verdict']}.") f"partition_ok={cov['partition_ok']} · verdict={bundle['verdict']}.")
return 0
def main(argv=None) -> None: def main(argv=None) -> int:
p = argparse.ArgumentParser( p = argparse.ArgumentParser(
prog="acceptance_gen", prog="acceptance_gen",
description="Matrice d'acceptation / traçabilité MVP (roadmap → preuve).") description="Matrice d'acceptation / traçabilité MVP (roadmap → preuve).")
@@ -322,8 +324,8 @@ def main(argv=None) -> None:
v.set_defaults(func=cmd_validate) v.set_defaults(func=cmd_validate)
args = p.parse_args(argv) args = p.parse_args(argv)
args.func(args) return args.func(args)
if __name__ == "__main__": if __name__ == "__main__":
main() raise SystemExit(main())
@@ -41,8 +41,7 @@
"rbac/fixtures_gen", "rbac/fixtures_gen",
"rbac/userperm_gen", "rbac/userperm_gen",
"rbac/roleprofile_gen", "rbac/roleprofile_gen",
"rbac/apply_plan", "rbac/apply_plan"
"faisabilite/generator"
], ],
"evidence_artifacts": [], "evidence_artifacts": [],
"out_of_scope": [ "out_of_scope": [
@@ -59,7 +58,7 @@
"roadmap_line": 46, "roadmap_line": 46,
"statement": "P07 Aqua Terra faisabilité complète auto-générée en <1h", "statement": "P07 Aqua Terra faisabilité complète auto-générée en <1h",
"status": "in_repo", "status": "in_repo",
"evidence_modules": ["faisabilite/bancable"], "evidence_modules": ["faisabilite/bancable", "faisabilite/generator"],
"evidence_artifacts": [], "evidence_artifacts": [],
"out_of_scope": [ "out_of_scope": [
{ {
@@ -33,7 +33,6 @@
{ {
"sprint": "S2", "sprint": "S2",
"expected": [ "expected": [
"faisabilite/generator",
"publiciste", "publiciste",
"rbac", "rbac",
"rbac/apply_plan", "rbac/apply_plan",
@@ -42,7 +41,6 @@
"rbac/userperm_gen" "rbac/userperm_gen"
], ],
"cited": [ "cited": [
"faisabilite/generator",
"publiciste", "publiciste",
"rbac", "rbac",
"rbac/apply_plan", "rbac/apply_plan",
@@ -57,10 +55,12 @@
{ {
"sprint": "S3", "sprint": "S3",
"expected": [ "expected": [
"faisabilite/bancable" "faisabilite/bancable",
"faisabilite/generator"
], ],
"cited": [ "cited": [
"faisabilite/bancable" "faisabilite/bancable",
"faisabilite/generator"
], ],
"missing": [], "missing": [],
"extra": [], "extra": [],
@@ -62,11 +62,6 @@
"module": "rbac/apply_plan", "module": "rbac/apply_plan",
"ci_job": "rbac-applyplan-tests", "ci_job": "rbac-applyplan-tests",
"gated": true "gated": true
},
{
"module": "faisabilite/generator",
"ci_job": "faisabilite-gen-tests",
"gated": true
} }
], ],
"artifacts": [], "artifacts": [],
@@ -76,7 +71,7 @@
"source": "CLAUDE.md #8 (VPS) · #1 (ERPNext natif = priorité — import via bench par agent Backend)" "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_modules_gated": true,
"all_artifacts_exist": true, "all_artifacts_exist": true,
"has_evidence": true "has_evidence": true
@@ -93,6 +88,11 @@
"module": "faisabilite/bancable", "module": "faisabilite/bancable",
"ci_job": "bancable-tests", "ci_job": "bancable-tests",
"gated": true "gated": true
},
{
"module": "faisabilite/generator",
"ci_job": "faisabilite-gen-tests",
"gated": true
} }
], ],
"artifacts": [], "artifacts": [],
@@ -102,7 +102,7 @@
"source": "CLAUDE.md #8 (VPS) — le rendu 3D/BIM tourne hors repo (bim-cloud.otov7.com)" "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_modules_gated": true,
"all_artifacts_exist": true, "all_artifacts_exist": true,
"has_evidence": 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 ## 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** : **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 retirer des tests, casser un `MANIFEST` ou supprimer un schéma fait chuter le
module sous 95 → `FAIL`. module sous 95 → `FAIL`.
@@ -40,42 +40,6 @@
} }
], ],
"modules": [ "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", "id": "publiciste",
"path": "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", "id": "crm-commissions",
"path": "crm/commissions", "path": "crm/commissions",
@@ -774,7 +774,7 @@
"criterion": "DOC", "criterion": "DOC",
"weight": 20, "weight": 20,
"passed": true, "passed": true,
"evidence": "README.md (3900 octets)" "evidence": "README.md (3901 octets)"
}, },
{ {
"criterion": "CONTRAT", "criterion": "CONTRAT",
@@ -858,7 +858,7 @@
"criterion": "DOC", "criterion": "DOC",
"weight": 20, "weight": 20,
"passed": true, "passed": true,
"evidence": "README.md (5468 octets)" "evidence": "README.md (5512 octets)"
}, },
{ {
"criterion": "CONTRAT", "criterion": "CONTRAT",
@@ -900,7 +900,7 @@
"criterion": "DOC", "criterion": "DOC",
"weight": 20, "weight": 20,
"passed": true, "passed": true,
"evidence": "README.md (4022 octets)" "evidence": "README.md (4015 octets)"
}, },
{ {
"criterion": "CONTRAT", "criterion": "CONTRAT",
@@ -63,7 +63,7 @@
"modules": [ "modules": [
{ "id": "publiciste", "path": "publiciste", "sprint": "S2", "archetype": "parser", "source": "roadmap Sprint 2 · Publiciste (parser faisabilité → projets_master.json) · ci.yml publiciste-tests" }, { "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": "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-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-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" }, { "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" m["verdict"] = "PASS" if m["score"] >= 95 else "FAIL"
bad["totals"]["fail"] = sum(1 for x in bad["modules"] bad["totals"]["fail"] = sum(1 for x in bad["modules"]
if x["verdict"] != "PASS") 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" bad["verdict"] = "FAIL"
errs = gen.check_invariants(bad, self.spec) 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) self.assertTrue(any("< seuil 4Big" in e for e in errs), errs)
def test_self_module_in_scope_is_caught(self): 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 `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 **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 `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é `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 byte-for-byte** avec le fichier commité (+ verdict `PASS`) — c'est ce qui empêche
@@ -8,10 +8,10 @@
"path": "crm/commissions", "path": "crm/commissions",
"expected_methods": 25, "expected_methods": 25,
"ran": 25, "ran": 25,
"passed": 25, "passed": 24,
"failures": 0, "failures": 0,
"errors": 0, "errors": 0,
"skipped": 0, "skipped": 1,
"ok": true, "ok": true,
"returncode": 0 "returncode": 0
}, },
@@ -20,10 +20,10 @@
"path": "crm/dossier_vente", "path": "crm/dossier_vente",
"expected_methods": 31, "expected_methods": 31,
"ran": 31, "ran": 31,
"passed": 31, "passed": 30,
"failures": 0, "failures": 0,
"errors": 0, "errors": 0,
"skipped": 0, "skipped": 1,
"ok": true, "ok": true,
"returncode": 0 "returncode": 0
}, },
@@ -44,10 +44,10 @@
"path": "crm/workflow_vente", "path": "crm/workflow_vente",
"expected_methods": 25, "expected_methods": 25,
"ran": 25, "ran": 25,
"passed": 25, "passed": 24,
"failures": 0, "failures": 0,
"errors": 0, "errors": 0,
"skipped": 0, "skipped": 1,
"ok": true, "ok": true,
"returncode": 0 "returncode": 0
}, },
@@ -80,10 +80,10 @@
"path": "faisabilite/bancable", "path": "faisabilite/bancable",
"expected_methods": 22, "expected_methods": 22,
"ran": 22, "ran": 22,
"passed": 22, "passed": 21,
"failures": 0, "failures": 0,
"errors": 0, "errors": 0,
"skipped": 0, "skipped": 1,
"ok": true, "ok": true,
"returncode": 0 "returncode": 0
}, },
@@ -92,10 +92,10 @@
"path": "faisabilite/generator", "path": "faisabilite/generator",
"expected_methods": 16, "expected_methods": 16,
"ran": 16, "ran": 16,
"passed": 16, "passed": 15,
"failures": 0, "failures": 0,
"errors": 0, "errors": 0,
"skipped": 0, "skipped": 1,
"ok": true, "ok": true,
"returncode": 0 "returncode": 0
}, },
@@ -104,10 +104,10 @@
"path": "fiscal/ecf_dgii", "path": "fiscal/ecf_dgii",
"expected_methods": 39, "expected_methods": 39,
"ran": 39, "ran": 39,
"passed": 39, "passed": 38,
"failures": 0, "failures": 0,
"errors": 0, "errors": 0,
"skipped": 0, "skipped": 1,
"ok": true, "ok": true,
"returncode": 0 "returncode": 0
}, },
@@ -128,10 +128,10 @@
"path": "frontend/portails", "path": "frontend/portails",
"expected_methods": 19, "expected_methods": 19,
"ran": 19, "ran": 19,
"passed": 19, "passed": 18,
"failures": 0, "failures": 0,
"errors": 0, "errors": 0,
"skipped": 0, "skipped": 1,
"ok": true, "ok": true,
"returncode": 0 "returncode": 0
}, },
@@ -140,10 +140,10 @@
"path": "legal/confotur", "path": "legal/confotur",
"expected_methods": 44, "expected_methods": 44,
"ran": 44, "ran": 44,
"passed": 44, "passed": 43,
"failures": 0, "failures": 0,
"errors": 0, "errors": 0,
"skipped": 0, "skipped": 1,
"ok": true, "ok": true,
"returncode": 0 "returncode": 0
}, },
@@ -159,15 +159,27 @@
"ok": true, "ok": true,
"returncode": 0 "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", "id": "publiciste",
"path": "publiciste", "path": "publiciste",
"expected_methods": 23, "expected_methods": 23,
"ran": 23, "ran": 23,
"passed": 23, "passed": 21,
"failures": 0, "failures": 0,
"errors": 0, "errors": 0,
"skipped": 0, "skipped": 2,
"ok": true, "ok": true,
"returncode": 0 "returncode": 0
}, },
@@ -212,10 +224,10 @@
"path": "rbac", "path": "rbac",
"expected_methods": 10, "expected_methods": 10,
"ran": 10, "ran": 10,
"passed": 10, "passed": 9,
"failures": 0, "failures": 0,
"errors": 0, "errors": 0,
"skipped": 0, "skipped": 1,
"ok": true, "ok": true,
"returncode": 0 "returncode": 0
}, },
@@ -224,10 +236,10 @@
"path": "rbac/apply_plan", "path": "rbac/apply_plan",
"expected_methods": 16, "expected_methods": 16,
"ran": 16, "ran": 16,
"passed": 16, "passed": 15,
"failures": 0, "failures": 0,
"errors": 0, "errors": 0,
"skipped": 0, "skipped": 1,
"ok": true, "ok": true,
"returncode": 0 "returncode": 0
}, },
@@ -236,10 +248,10 @@
"path": "rbac/fixtures_gen", "path": "rbac/fixtures_gen",
"expected_methods": 11, "expected_methods": 11,
"ran": 11, "ran": 11,
"passed": 11, "passed": 10,
"failures": 0, "failures": 0,
"errors": 0, "errors": 0,
"skipped": 0, "skipped": 1,
"ok": true, "ok": true,
"returncode": 0 "returncode": 0
}, },
@@ -248,10 +260,10 @@
"path": "rbac/roleprofile_gen", "path": "rbac/roleprofile_gen",
"expected_methods": 11, "expected_methods": 11,
"ran": 11, "ran": 11,
"passed": 11, "passed": 10,
"failures": 0, "failures": 0,
"errors": 0, "errors": 0,
"skipped": 0, "skipped": 1,
"ok": true, "ok": true,
"returncode": 0 "returncode": 0
}, },
@@ -260,10 +272,10 @@
"path": "rbac/userperm_gen", "path": "rbac/userperm_gen",
"expected_methods": 12, "expected_methods": 12,
"ran": 12, "ran": 12,
"passed": 12, "passed": 11,
"failures": 0, "failures": 0,
"errors": 0, "errors": 0,
"skipped": 0, "skipped": 1,
"ok": true, "ok": true,
"returncode": 0 "returncode": 0
}, },
@@ -272,23 +284,23 @@
"path": "seo", "path": "seo",
"expected_methods": 36, "expected_methods": 36,
"ran": 36, "ran": 36,
"passed": 36, "passed": 35,
"failures": 0, "failures": 0,
"errors": 0, "errors": 0,
"skipped": 0, "skipped": 1,
"ok": true, "ok": true,
"returncode": 0 "returncode": 0
} }
], ],
"totals": { "totals": {
"suites": 23, "suites": 24,
"green": 23, "green": 24,
"red": 0, "red": 0,
"ran": 604, "ran": 624,
"passed": 604, "passed": 607,
"failures": 0, "failures": 0,
"errors": 0, "errors": 0,
"skipped": 0 "skipped": 17
}, },
"verdict": "PASS" "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]: 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. """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 Byte-déterministe (aucun horodatage/hôte/chemin absolu ; suites lancées sous
`regression_run.json` EST commité et byte-gaté par `ci/check_regression.sh`.""" `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) suites = discovery.discover_suites(spec)
discover_cmd = list(spec["discover_cmd"]) discover_cmd = list(spec["discover_cmd"])
# `python3 -m unittest ...` → on retire le binaire, on garde les args. # `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 in-process provoquerait des collisions de modules. Le subprocess reproduit
fidèlement l'environnement du CI. 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`/`tryexcept 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 `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). 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 `abs_path` = répertoire du module (contenant `tests/`). Aucune sortie n'est
imprimée ici l'agrégateur décide de l'affichage.""" imprimée ici l'agrégateur décide de l'affichage."""
py = python or sys.executable 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") tests_dir = os.path.join(abs_path, "tests")
if not os.path.isdir(tests_dir): if not os.path.isdir(tests_dir):
return { return {
@@ -25,9 +25,12 @@ Sous-commandes :
live + verdict ; sort en erreur (code 0) si une suite est live + verdict ; sort en erreur (code 0) si une suite est
rouge. Écrit `regression_run.json` : bien qu'il porte les rouge. Écrit `regression_run.json` : bien qu'il porte les
compteurs réellement exécutés, il ne contient AUCUN compteurs réellement exécutés, il ne contient AUCUN
horodatage/hôte/chemin absolu deux `run` sont horodatage/hôte/chemin absolu, ET chaque suite tourne sous
byte-identiques, donc l'artefact EST commité et byte-gaté `python -S` (oracle tiers optionnel `jsonschema` neutralisé,
par `ci/check_regression.sh` (toute dérive = CI rouge). 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 Toutes les sous-commandes productrices sont déterministes (tri stable, aucun
horodatage) artefacts diffables + commités + byte-gatés : `regression_plan.json` 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 ## Tests
```bash ```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) ## 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) ## ✅ 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 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** 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) 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 ## 2. Faisabilité Agent — ressources existantes
- `/opt/oto/oto_agent_faisabilite.py` (compilé pyc) ⭐ AGENT DÉDIÉ - `/opt/oto/oto_agent_faisabilite.py` (compilé pyc) ⭐ AGENT DÉDIÉ
- `/opt/oto/oto_orchestrateur_faisabilite.py` (compilé pyc) - `/opt/oto/oto_orchestrateur_faisabilite.py` (compilé pyc)
- `/opt/oto/oto_module_etudes_faisabilite_real.py` - `/opt/oto/oto_module_etudes_faisabilite_real.py` (compilé pyc)
- `/opt/oto/oto_module_execution_faisabilite.py` - `/opt/oto/oto_module_execution_faisabilite.py` (compilé pyc)
- `/opt/oto/oto_module_faisabilites_pages.py` - `/opt/oto/oto_module_faisabilites_pages.py` (compilé pyc)
- `/opt/oto/oto_module_faisabilites_viewer.py` - `/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/capabilities/knowledge/faisabilite_4_volets_standard.md` ⭐ standard 4 volets
- `/opt/oto/otoia/otoia_batch_projets.py` — batch faisabilités multi-projets - `/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 ## 8. Mobile Agent — ressources existantes
- `/opt/oto/oto_module_mobile_download.py` - `/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/deploy_mobile_rbac.sh`
- `/opt/oto/staging/mobile_rbac/seed_mobile_rbac.py` - `/opt/oto/staging/mobile_rbac/seed_mobile_rbac.py`
- `/opt/oto/static/oto_mobile.js` - `/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 | | [`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) | | [`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 | | [`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 | | [`02_master_prompt/`](02_master_prompt) | Prompt maître du mandat |
| [`03_agents/`](03_agents) | Fiches des **13 agents** (rôle · livrables · coordination) | | [`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/`](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/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é | | [`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 | | [`.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) | | [`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é) ## É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). — 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), 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 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 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 »), 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). 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. - **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 ## 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 worker** (`CLAUDE.md` #8). Le plan d'activation ordonné est le run-book
[`devops/deploy_runbook`](05_deliverables_mvp/devops/deploy_runbook/README.md). [`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 ## Git — Gitea uniquement
Dépôt de mandat : `michel/oto-enterprise-os-dtp` sur Gitea (`153.75.250.214:3015`). 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-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-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-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 | | `gate` | — | Agrégat vert = gate qualité 4Big franchi | ✅ oui |
Aucune dépendance réseau/marketplace hors `actions/checkout`. Tout tourne avec 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 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). 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) ### 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 : 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 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 ; `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 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 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) : (paraphrase). Cohérences croisées en bonus (mordent un plan interne incohérent) :
ordres contigus `1..N` sans doublon · `responsable` ∈ {worker, vps, worker+vps} · 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 disparaître un chiffre est elle-même une régression de traçabilité). stdlib pur
(bash/git/python3), zéro réseau. (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) ### `lib.sh` — helper partagé (sourcé, jamais exécuté seul)
Les gates commençaient tous par `cd "$(git rev-parse --show-toplevel)"`. **Hors** 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), 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 # 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, # `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` + # 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é # 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 # 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 # 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 # 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 # `<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 # 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 # 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 # 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 # 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 # 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 # 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. # 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 # 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"]` == # 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 # 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. # 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/ # 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 # 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 # 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 # l'artefact} (aucune phase FANTÔME/MANQUANTE) ET l'UNION des cellules Modules ==
# 22 modules de l'artefact (aucun module OUBLIÉ/en trop). La colonne « Phase » reste # 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 # libre (paraphrase). Cohérences croisées en bonus (mordent un plan INTERNEMENT
# incohérent) : ordres contigus 1..N sans doublon · responsable ∈ {worker, vps, # 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 # 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: else:
_cont_shape = re.compile(r"[a-z0-9]+-(?:" + "|".join(_stems) + r")-1") _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") _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( vps_files = [f for f in subprocess.run(
["git", "ls-files", "*.md"], ["git", "ls-files", "*.md"],
capture_output=True, text=True).stdout.split() capture_output=True, text=True).stdout.split()
@@ -8141,10 +8151,12 @@ if _vps_cm is not None:
except OSError as e: except OSError as e:
bad(f"VPS infra · {vf} illisible : {e}"); continue bad(f"VPS infra · {vf} illisible : {e}"); continue
# (a) IPv4 : toute IP citée == IP canonique du §VPS # (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)) ips = set(_ip_shape.findall(vtxt))
if ips: if ips:
ip_seen += 1 ip_seen += 1
stale_ip = sorted(ips - {vps_ip}) stale_ip = sorted(ips - {vps_ip} - _backup_ips)
if stale_ip: if stale_ip:
bad(f"VPS infra · {vf} cite l'IP périmée {stale_ip} MAIS " bad(f"VPS infra · {vf} cite l'IP périmée {stale_ip} MAIS "
f"CLAUDE.md §VPS dit `{vps_ip}` → serveur migré / IP " f"CLAUDE.md §VPS dit `{vps_ip}` → serveur migré / IP "
+6 -1
View File
@@ -22,7 +22,12 @@
# rend impossible à re-commettre. # rend impossible à re-commettre.
# #
# Déterminisme : `regression_run.json` ne contient AUCUN horodatage / hôte / # 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. # 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 # Contrat vérifié : `regression_gen.py run` régénéré vers un tmp DOIT produire un