Compare commits

..

50 Commits

Author SHA1 Message Date
Claude Code DTP Worker f954cc19d1 [DTP-Worker 20260802_183604] Auto exec · session 20260802_183604
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 / 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 / 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 / E2E baseline Playwright (manuel) (push) Has been cancelled
CI / Gate qualité (agrégat) (push) Has been cancelled
2026-08-02 18:49:35 +00:00
Claude Code DTP Worker 97d3b64780 [DTP-Worker 20260802_183604] Sprint 8 · buffer · README racine (dérive doc-vs-réalité non gatée) : la puce « Régression » affirmait regression_run.json « non commité par design » → FAUX depuis la refonte baseline byte-gatée (76fb0e7)
Correction de VÉRITÉ (PAS un gate #5, PAS un chiffre inventé #6).

Constat : README.md:44-46 déclarait le compte agrégé « produit par qa/regression
run (non commité par design) ». Or regression_run.json EST commité (git ls-files le
confirme) et sert de baseline byte-identique au gate ci/check_regression.sh (mécanisme
introduit par 76fb0e7) ; le README du module (qa/regression/README.md:50,57-61) ET le
commentaire du gate lui-même (ci/check_readme_claims.sh:264,269 « commité · déterministe
· compte agrégé faisant autorité ») le disent. Le README racine reflétait l'ANCIEN
design pré-76fb0e7.

Angle mort : check_readme_claims ancre le nombre « 22 suites gated » (recompté de
regression_plan.totals.suites) mais PAS le fait-en-clair « (non commité par design) » ;
check_docs ne valide que les liens. La dérive vivait dans l'interstice.

Fix : puce réécrite vers la réalité — regression_run.json désigné artefact « commité et
byte-gaté », lien ajouté ; token gaté **22 suites gated** conservé verbatim ; lien
regression_plan.json préservé ; « non commité par design » supprimé. Aucun 564 hardcodé
(pas de surface non gatée nouvelle · fidèle à « ce README n'introduit aucune donnée nouvelle »).

Vérif : run_ci.sh 30 PASS / 0 FAIL / 0 SKIP (check_docs résout le nouveau lien commité ·
check_readme_claims retrouve 22 suites gated · guard_constraints vert). Zéro gate ajouté,
zéro fichier de code touché, aucune commande VPS.

Note : dir scratch untracked 05_deliverables_mvp/frontend/portails/out_fresh/ (byte-identique
à out/ commité, référencé par aucun code) laissé en place — authorship non confirmé, non
supprimé par prudence (jamais git clean · interdit #1).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 18:47:41 +00:00
Claude Code DTP Worker 3ff51d94fb [DTP-Worker 20260802_180604] Sprint 8 · buffer · Reporting stakeholder (canal 2) : rattrapage du snapshot daily_reports/2026-08-02 sur les 3 sessions de retouches doc/outillage depuis la clôture 160601 (59eb4dc/68f8c1a/d2ab269) → 4e addendum ADDITIF (corps + 3 addenda intacts), chaque chiffre re-sourcé par recompute python3 indépendant (22/22 scores={100} · 564/564 · 15/15 in_repo=true · 13 fiches · gate.needs=30) · audit doc-vs-réalité relancé cette session = aucune dérive · zéro nouveau module · zéro gate ajouté (#5) · run_ci.sh 30 PASS + check_docs/check_readme_claims/guard_constraints verts
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 18:12:40 +00:00
Claude Code DTP Worker d2ab269684 [DTP-Worker 20260802_173603] Sprint 8 · buffer · ci/README.md : retrait des comptes CI figés « 7 gates / 23 suites »
Même fix que dcb463e, un cran plus haut. dcb463e avait dérivé le décompte 30/23/7
du COMMENTAIRE de run_ci.sh (il se périme à chaque module ajouté/retiré de ci.yml) ;
mais la DOC du lanceur (ci/README.md §« Rejouer le gate EN LOCAL ») re-transcrivait
encore les mêmes nombres à la main : bloc d'usage L34-35 (« 7 gates statiques + 23
suites de module » / « 7 gates ci/*.sh ») + checklist L1601 (« 7 gates statiques »).

Vérif d'exposition (verify-uncovered-before-gating) : mutation 23→24 / 7→8 →
check_readme_claims ET check_docs restent VERTS → surface réellement NON gatée.

Décision FIXER pas gater (fix-vs-gate + local-ci-runner) : ci/README.md:51-54 déclare
lui-même que run_ci.sh « n'a pas à être re-gaté (#5) » (exactitude structurelle) —
gater la prose qui le décrit trahirait cette posture. On retire donc les nombres
figés et on laisse run_ci.sh seul propriétaire du décompte (dérivé de gate.needs,
imprimé à l'exécution + --list). Ajout d'un paragraphe l'explicitant (même raison #6).

Intactes : les « 7 gates re-verts » du CHANGELOG (narratif historique d'états passés
VRAIS, pas une composition vive). Zéro module · zéro gate (#5) · zéro chiffre inventé
(#6) · run_ci.sh 30 PASS · guards verts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 17:41:25 +00:00
Claude Code DTP Worker 68f8c1a718 [DTP-Worker 20260802_170601] Sprint 8 · buffer · run_ci.sh : -h|--help + en-tête --static honnête (UX du lanceur, PAS un gate #5)
Deux vraies aspérités d'UX sur le lanceur run_ci.sh (miroir local du gate),
reproduites avant fix :
- `--help`/`-h` échouait « option inconnue » exit 2 alors qu'un bloc d'usage
  riche dormait dans l'en-tête → ajout usage() branché AVANT résolution git
  (aide marche hors checkout : exit 0 ; vrai run hors git reste exit 3),
  codes de sortie documentés (0/1/2/3).
- en mode --static l'en-tête annonçait « 30 jobs = 7 gates + 23 suites »
  alors que seuls 7 gates tournent → récap « 7 PASS » contredisait l'en-tête ;
  en-tête rendu mode-conscient (« 7 gates seuls, 23 suites ignorées »).
- message d'option inconnue + ci/README.md alignés (ajout --help).

Toutes les valeurs restent DÉRIVÉES de gate.needs (#6, zéro compte figé).
Zéro nouveau module · zéro gate ajouté (#5) : run_ci.sh reste un lanceur
(racine ∉ ci/*.sh, hors gate.needs, INV-B). Vérif : --help exit 0 in+hors git ·
--bogus exit 2 · check_docs/check_readme_claims/guard_constraints verts ·
run_ci.sh plein → 30 PASS · 0 FAIL · 0 SKIP.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 17:11:19 +00:00
Claude Code DTP Worker 59eb4dcb7c [DTP-Worker 20260802_163601] Sprint 8 · buffer · Fiche devops/AGENT.md : documentation du livrable run_ci.sh (indexé au README racine mais absent de la fiche de son agent propriétaire) → 3e ligne table + sous-section « outillage local, PAS un gate #5 » (dérive de gate.needs, hors gate.needs, racine ∉ ci/ donc hors INV-B), aligné mémoire local-ci-runner · zéro module · zéro gate · check_docs valide le lien ../../run_ci.sh · run_ci 30 PASS + guards verts 2026-08-02 16:39:22 +00:00
Claude Code DTP Worker 4f390b4ee2 [DTP-Worker 20260802_160601] Sprint 8 · buffer · Reporting stakeholder (canal 2) : rattrapage du snapshot daily_reports/2026-08-02 sur les 2 sessions run_ci.sh (49123ec/dcb463e) → 3e addendum ADDITIF (corps+2 addenda intacts), chaque chiffre re-sourcé par recompute python3 indépendant (22/22 scores={100} · 564/564 · 15/15 in_repo=true · 13 fiches · gate.needs=30) · zéro nouveau module · zéro gate ajouté (#5) · seul livrable net = outil DevOps run_ci.sh déjà gaté par construction · run_ci.sh 30 PASS + check_docs conforme
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 16:11:18 +00:00
Claude Code DTP Worker dcb463ee00 [DTP-Worker 20260802_153601] Sprint 8 · buffer · run_ci.sh : décompte 30/23/7 DÉRIVÉ à l'exécution (retrait des comptes figés du commentaire)
Dernière surface figée dans l'outil « anti-dérive par construction » : run_ci.sh
dérive déjà la liste des checks de gate.needs (zéro liste en dur) mais son
commentaire d'en-tête re-transcrivait à la main « 30 jobs · 23 suites · 7 gates »
— un « vert trompeur » de la classe qu'il combat, logé dans sa propre doc
(ajouter un job module → le script s'adapte, le commentaire se périme en silence).

FIX (éliminer le nombre figé), pas gate N+1 : un lanceur de commodité (root ∉
ci/*.sh) ne mérite pas un gate redondant (#5) qui prendrait la mauvaise autorité.
- ventilation dérivée des mêmes ROWS que l'exécution (n_gates/n_suites/n_jobs) ;
- en-tête d'exécution + pied de --list impriment « 30 = 7 + 23 » (toujours exact) ;
- commentaire reformulé : littéraux 30/23/7 remplacés par la description
  qualitative bash ci/*.sh / python3 -m unittest + note « décompte DÉRIVÉ, jamais
  figé ici » (anti-invention CLAUDE.md #6).

Vérif : --list/--static impriment 30=7+23 · run_ci.sh complet 30 PASS/0 FAIL/0 SKIP ·
7 gates re-joués exit 0. Aucun gate ajouté (#5). Aucune commande touchant au VPS.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 15:42:38 +00:00
Claude Code DTP Worker 49123ecba3 [DTP-Worker 20260802_150601] Sprint 8 · buffer · DevOps outillage : run_ci.sh — miroir LOCAL du gate CI dérivé de gate.needs (zéro liste en dur), reproduit les 30 jobs (7 gates ci/*.sh + 23 suites module) en une commande · anti-dérive par construction (suit ci.yml) · PAS un gate (#5), root ∉ ci/*.sh donc hors INV-B · parseur awk anti-commentaire · chemin d'échec prouvé (EXIT=1 + dump) · doc README + ci/README · 7/7 gates statiques verts
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 15:16:29 +00:00
Claude Code DTP Worker 1c0cb8e973 [DTP-Worker 20260802_143555] Sprint 8 · buffer · Reporting stakeholder : rattrapage du snapshot daily_reports/2026-08-02 sur les 4 sessions buffer intercalées depuis la clôture 120544 (cb8d931/8dcedb0/576e78b/bd3572b) → 2e addendum additif, chaque chiffre re-sourcé par recompute python3 (22/22 · 564/564 · 15/15 · 13 fiches · gate.needs=30) · zéro gate ajouté (#5) · faux positif CLAUDE.md#2 « otov7-platform » écarté (repo plateforme réel ≠ repo mandat, pas une dérive)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 14:42:56 +00:00
Claude Code DTP Worker bd3572b382 [DTP-Worker 20260802_140554] Sprint 8 · buffer · Audit d'EFFICACITÉ des gates : mutation-testing du suite → 7 mutations / 7 RED prouvés (« vert » ≠ « faux-vert »), zéro gate ajouté (#5)
Angle complémentaire à la campagne « surfaces non couvertes » (verify-uncovered-
before-gating prouve qu'une dérive SERAIT attrapée si un gate mordait) : en phase
launch, les gates VERTS mordent-ils réellement ? J'ai mutation-testé le suite, chaque
mutation immédiatement révertée (arbre propre re-vérifié) :
- check_ci_integrity : job fantôme dans gate.needs (DANGLING) → RED · job réel retiré
  de gate.needs (MISSING) → RED
- check_artifacts : octet parasite dans out/MANIFEST.json (repro byte) → RED
- check_readme_claims : README « 22/22 modules »→« 23/22 » → RED · « 15 promesses »
  →« 16 » → RED
- guard_constraints : URL github.com (usage réel) → RED · « stripe payments » → RED
7/7 : les 4 gates à oracle recomputé MORDENT ; « vert » = réellement conforme.

Non-finding documenté (piège évité) : guard_constraints ne flague PAS le mot nu
« GitHub », à dessein (terme interdit = l'USAGE github.com|git@github, pas la mention —
mémoire guard-constraints-flags-usage-not-mention). Ma 1re mutation « bare word » est
restée verte → ce n'était pas un trou mais une mutation mal conçue ; re-testé avec un
vrai usage → RED.

Zéro gate ajouté (aucune surface non couverte prouvée · #5), seul fichier touché = le
log (ligne M5b citant github.com marquée ci-allow, cf. guard-constraints-log-prose).
7 gates re-joués → exit 0. Aucune commande touchant au VPS.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 14:14:36 +00:00
Claude Code DTP Worker 576e78b6a9 [DTP-Worker 20260802_133549] Sprint 8 · buffer · roles_couverts (« 44 ») : enquête anti-dérive → NON-surface prouvée (3 manifestes importent la même fonction roles_by_portail sur l'unique contrat rbac_50_roles.json → transitivement mono-sourcé, un cross-gate serait redondant #5, contra le cross-check RBAC-50 qui re-dérive) + documentation du garde-fou dans ci/README.md pour prévenir un gate N+1 redondant 2026-08-02 13:45:38 +00:00
Claude Code DTP Worker 8dcedb092c [DTP-Worker 20260802_130548] Sprint 8 · buffer · Compte d'agents : ancrage des 3 surfaces vives de GAP_ANALYSIS_SPRINT1 (dont coche couverture DoD) sur n_agents
Balayage de complétude après cb8d931 (qui a ancré « 13 agents » aux 2 docs de
mandat de tête) : 3ᵉ surface HORS gate = le livrable Sprint 1 GAP_ANALYSIS_SPRINT1.md
(Definition of Done). Retranscrit le compte à 3 endroits vifs — périmètre d'audit
(l.12) · constat global total (l.53, le « 12 » analytique EXCLU) · coche de
couverture DoD (l.214). Prouvé ungate (grep ci/=0 ; mutation 13→14 laissait exit 0)
et à dérive prouvée (publiciste 12→13). Extension de la boucle head-docs existante
(3 tuples réutilisant n_agents = git ls-files 03_agents/*/AGENT.md · zéro nouvelle
machinerie · #5) ; on n'ancre que le total ; INTROUVABLE = échec (#6). ci/README.md
documente désormais 7 surfaces vives. 6 morsures (M5 no-false-positive sur le « 12 » ·
M6 SILENT-GREEN via git rm --cached prouvant l'autorité git-index). 7 gates exit 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 13:11:37 +00:00
Claude Code DTP Worker cb8d931abb [DTP-Worker 20260802_123546] Sprint 8 · buffer · Dérive doc-vs-réalité « 12 agents » sur les 2 docs de mandat de tête (AGENTS_EXISTING_ASSETS titre + MASTER_PROMPT §28) : le 13e agent (publiciste) est livré+gaté 100/100 → correction 12→13 (source unique 03_agents/) + clarification statut publiciste (LIVRÉ mandat · runtime VPS hors #8) + extension check_readme_claims réutilisant n_agents pour ancrer ces 2 docs (surface prouvée non couverte par mutation-test) 2026-08-02 12:42:52 +00:00
Claude Code DTP Worker 2e71c5f51e [DTP-Worker 20260802_120544] Sprint 8 · buffer · Clôture de journée du snapshot daily_reports/2026-08-02 : addendum additif actant la fermeture 13/13 fiches AGENT.md (commit 2bc3afb) → lève la réserve −4 « 2/13 auditées » et la « prochaine tâche #2 » devenues caduques
Snapshot stakeholder du jour (écrit 11:11) rendu véridique sans nouveau fichier
per-session (anti-doublon #5) : corps historique 110539 intact, addendum re-vérifié.
Recompute indépendant concordant (22/22 qualité · 564/564 régression · 15/15 recette
· 13 agents) ; 7 gates CI re-joués exit 0. Aucune action VPS (#8).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 12:10:15 +00:00
Claude Code DTP Worker 2bc3afbd1a [DTP-Worker 20260802_113542] Sprint 8 · buffer · Audit exactitude doc-vs-réalité des 11 fiches AGENT.md restantes : 0 dérive réelle (13/13 fiches désormais auditées), 2 « dérives » signalées levées comme faux positifs de convention (onapi_legal backtick deliverables-relative miroir du code · faisabilite chemins VPS/futurs assumés) + 1 clarification de lisibilité (bandeau VPS-runtime vs template committé) sur faisabilite/AGENT.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 11:43:45 +00:00
Claude Code DTP Worker 519f6dbfee [DTP-Worker 20260802_110539] Sprint 8 · buffer · Reprise du canal daily_reports/ (lapsus depuis 30/07) : instantané MVP launch-readiness 100% sourcé (22/22 qualité · 564/564 régression · 15/15 recette) + audit doc-vs-réalité fiches crm/bim + réconciliation des 2 canaux de journalisation
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 11:12:10 +00:00
Claude Code DTP Worker 4841b77099 [DTP-Worker 20260802_103535] Sprint 8 · buffer · Dérive doc-vs-réalité : 11 fiches AGENT.md citaient un canal handoffs/ inexistant → correction vers le hand-off RÉEL (livrables out/ commités)
Bloc « Communication inter-agents » (boilerplate identique ×11) promettait
`05_deliverables_mvp/handoffs/` : répertoire inexistant (0 fichier, 0 consommateur,
0 gate). Échappé à check_docs.sh car chemin en code-span backtick (neutralisé l.28,
à dessein — gater les backticks = faux positifs massifs, donc NON élargi). Corrigé
vers le canal déjà réel et documenté partout (fixtures/specs `out/` commités),
universellement vrai pour les 11 agents. 7 gates verts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 10:39:53 +00:00
Claude Code DTP Worker 0752e35988 [DTP-Worker 20260802_100534] Sprint 8 · buffer · Ancrage des MINIMA D'ORCHESTRATION de demo/scenarios (« ≥ 2 modules distincts · ≥ 3 beats ») sur la garde check_invariants (CODE-autorité)
Classe « seuil data-derived recopié dans une garde de validation + sa prose,
jamais ancré » (déjà couverte pour min_doc_bytes/min_test_methods de l'auditeur
4Big). Livrable Sprint 7 demo/scenarios : la garantie #3 du README énonce deux
planchers RÉELLEMENT imposés par check_invariants bloc (c) — len(sc["beats"])<3,
len(mods)<2 — mais recopiés à la main à 3 surfaces (comparaison `<`, message
d'erreur « (min N) », prose README), aucune gatée. Muter « ≥ 3 beats »→« ≥ 4 »
laissait check_readme_claims exit 0 (prouvé ungate).

Gate re-dérive les 2 minima depuis la SEULE comparaison `<` (source unique #5) et
exige message + prose == plancher effectif ; surface INTROUVABLE échoue (#6).
7 morsures adversariales (dont M3 SILENT-GREEN : code resserré `<3`→`<4` ⇒ message
ET README mordent, preuve autorité=CODE). 39 tests OK (générateur inchangé) ; suite
CI complète re-verte.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 10:11:34 +00:00
Claude Code DTP Worker 3cb1bf06e1 [DTP-Worker 20260802_093532] Sprint 8 · buffer · Ledger d'invariants 9ᵉ instance : gate d'IDENTITÉ du COMPTE de _validate_bundle de crm/workflow_vente (dialecte # Invariant N ·, 3ᵉ oubli de la classe « close »)
Le générateur workflow_vente_gen.py (Livrable Sprint 4 l.50) porte un ledger de
9 invariants métier de graphe dans `_validate_bundle` (marqueurs `# Invariant N ·`,
phrasé « schéma + 9 invariants »), avec le nombre « 9 » recopié à la main à 4
surfaces vives (gen l.223 + README l.78/114/125), aucune gatée. Prouvé ungate
avant ajout (README 9→10 laissait check_readme_claims exit 0). Gate ajouté
(a)(b)(d) sur le modèle Mobile : recompute le ledger depuis le SEUL registre-source
`# Invariant N ·`, exige contiguïté 1..N + toute mention « N invariants » du .py et
du README == |ledger|. Pas de gate (e) enum : la liste « ## Invariants vérifiés par
le CLI » compte le schéma en item 1 et fusionne les invariants 8&9 → n'est pas le
ledger (faux positif fragile évité). « 25 tests » exclu nativement par l'ancre
« invariants » (aucun faux positif). 3 checks verts arbre propre · 7 morsures
adversariales (dont M3 SILENT-GREEN `# Invariant 10 ·` ⇒ autorité=CODE, M7
no-false-positive) · unittest 25 OK · suite CI complète re-verte.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 09:42:13 +00:00
Claude Code DTP Worker ca15e7e12b [DTP-Worker 20260802_090530] Sprint 8 · buffer · Angle mort gate « comptes tests par suite » : la cellule rbac (fiche pointe une SPEC, pas un README) était non vérifiée
row_re du gate check_readme_claims §comptes-par-suite exigeait un lien …/README.md ;
la fiche erpnext_backend pointe rbac/ via RBAC_50_ROLES_SPEC.md (son point d'entrée
contrat), donc sa cellule « Tests | 10 | » échappait au recompute depuis
regression_plan.json. Mutation-test 10→99 : gate restait VERT (vrai angle mort,
classe « vert trompeur »). Extension : accepte tout *.md d'entrée du dossier module
(/[\w.-]+.md) ; le garde path-not-in-auth protège l'élargissement. rbac désormais
vue+mordue ; 7 gates verts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 09:10:39 +00:00
Claude Code DTP Worker 448c310398 [DTP-Worker 20260802_083529] Sprint 8 · buffer · Faisabilité/bancable : élimination du taux 0.52 codé en dur dans l'oracle _check_derived_arithmetic
Le seul survivant en dur du paramètre canonique #9 (point d'équilibre 52 %)
vivait au cœur de l'oracle anti-invention de bancable_gen.py — alors que le
module PRÊCHE la source unique (deps.py « une seule source de vérité » ;
finance._pct « jamais codé en dur »). Transitivement protégé (donc gate
redondant, #5) mais BUG LATENT inverse : #9 52 %→55 % aurait fait rejeter
en silence un dossier bancable VALIDE. Fix = dériver le taux de
CANONICAL (finance._pct), pas un checker de plus.

Vérif : validate byte-identique à 52 % (pe=21) · 22 tests verts · preuve
levée du bug (source→55 % ⇒ pe=22, invariants OK) · 6 gates CI exit 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 08:43:04 +00:00
Claude Code DTP Worker ccb86fe7ca [DTP-Worker 20260802_080527] Sprint 8 · buffer · Doc QA : documentation du 2ᵉ étage QA (batterie de gates statiques ci/*.sh) dans qa/AGENT.md — trou comblé (le titre promettait « gates de méta-niveau », le corps n'en tabulait aucun) ; vérif préalable : pas de dérive fiscal/ecf (ledger schéma-#1 synthétisé), pas de gate redondant à ajouter
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 08:13:27 +00:00
Claude Code DTP Worker 0f4f1b533b [DTP-Worker 20260802_073526] Sprint 8 · buffer · Ledger d'invariants 2ᵉ FAMILLE : ancrage du COMPTE # INVn ·/validate_bundle de deploy_runbook (11) + acceptance (11) — dialecte « N familles d'invariants » qui avait glissé la clôture
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 07:45:26 +00:00
Claude Code DTP Worker 655eeeb4a1 [DTP-Worker 20260802_070524] Sprint 8 · buffer · Mobile app_config : gate d'IDENTITÉ du COMPTE D'INVARIANTS de _validate_bundle (8ᵉ instance de la classe ledger)
Recompute le compte « 12 invariants » depuis le SEUL registre-source
(`# Invariant N ·` de `_validate_bundle`) — 4 surfaces prose hand-transcrites
(CLI .py, 2× README, docstring test) ancrées, aucune n'était gatée. Style de
marqueur `# Invariant N ·` + fonction `_validate_bundle` = deux différences
structurelles vs les 7 blocs `# N ·`/`_validate` précédents ; ajoute la surface
fichier-de-tests (b'). 4 verts arbre propre · 8 morsures adversariales · 6 gates
CI exit 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 07:11:34 +00:00
Claude Code DTP Worker 335d449346 [DTP-Worker 20260802_063519] Sprint 8 · buffer · RBAC SPEC §2 : ancrage de l'encadré « Défense en profondeur » (NOM du détenteur de set_user_permissions · scope · famille) sur l'artefact byte-gaté
Extension du bloc « RBAC fixtures » (points 4 & 5) : la 3e surface prose du fait
de sécurité set_user_permissions (SPEC §2 encadré + §6 row) était NON gatée. Le
test test_rbac impose scope/famille à tout détenteur mais jamais le NOM du rôle ni
la prose §2. Réutilise fx_sup (singleton déjà byte-gaté · pas de doublon d'unicité,
#5) comme nom-autorité + src_roles pour scope/famille.

Prouvé M6 SILENT-GREEN : rename détenteur dans le contrat + régen artefacts =>
check_artifacts VERT mais §2 name-gate RED (autorité = artefact, pas cohérence SPEC).
6 morsures adversariales, 6 gates re-verts exit 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 06:48:01 +00:00
Claude Code DTP Worker 215c9e6f25 [DTP-Worker 20260802_060518] Sprint 8 · buffer · Traçabilité : ancrage de la CITATION roadmap_ref VERBATIM (l.N + libellé) des 4 MANIFESTs sur la ligne réelle de la roadmap
Gate anti-dérive #6 : demo/scenarios · frontend/chat_otoia · frontend/portails ·
mobile/app_config publient un roadmap_ref « …l.A[-B] « <bullet> » » dans
out/MANIFEST.json (byte-gaté). check_artifacts ne prouve QUE spec==MANIFEST ;
INV11 n'ancre que les roadmap_line NUMÉRIQUES de qa/acceptance. Reformuler/
renuméroter la roadmap laissait les 4 citations périmées en silence (silent-green
prouvé : reword l.63 ⇒ 6 gates verts, chat_otoia cite toujours l'ancien texte).

check_readme_claims : découvre chaque MANIFEST à citation et exige (1) fichier ==
roadmap canonique, (2) libellé normalisé ⊆ lignes A..B réelles, (3) spec ==
MANIFEST ; 4 modules connus EXIGÉS présents (anti-évaporation). 5 checks verts ·
7 morsures (M1 silent-green reword roadmap · M2 renumber · M3 drift citation ·
M4 wrong file · M5 spec≠manifest · M6 évaporation · M7 hors-plage). Suite complète
exit 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 06:15:53 +00:00
Claude Code DTP Worker 0d4fd0942c [DTP-Worker 20260802_053514] Sprint 8 · buffer · RBAC SPEC §6 : ancrage de l'ÉNUMÉRATION des DocTypes custom (2ᵉ surface prose) sur le catalogue byte-gaté du contrat
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 05:49:04 +00:00
Claude Code DTP Worker 11c48b668b [DTP-Worker 20260802_050513] Sprint 8 · buffer · RBAC SPEC §2 : ancrage de l'ÉNUMÉRATION des scope_donnees (ligne Row-level) sur l'enum de rbac.schema.json
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 05:10:43 +00:00
Claude Code DTP Worker bcfc0f9a9d [DTP-Worker 20260802_043512] Sprint 8 · buffer · Faisabilité : ancrage du DOMAINE DES CODES PROJET (garde _validate_brief + 2 schémas JSON) sur CLAUDE.md §Projets
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 04:41:54 +00:00
Claude Code DTP Worker 03b481c6ee [DTP-Worker 20260802_040511] Sprint 8 · buffer · RBAC SPEC §3 : ancrage de la colonne « Familles rattachées » PAR portail sur rbac_50_roles.json
Nouvelle surface anti-dérive (#6) : la table §3 « Cartographie portails ↔
familles » porte deux colonnes data-derived du contrat ; « Nb rôles » était
gatée, « Familles rattachées » (SET des `famille` distinctes par portail) ne
l'était pas — le count-gate l'avale via `[^|]*`.

Gate « 1quater-bis » : recompute le SET de familles par portail depuis
rbac_50_roles.json[roles][].famille, égalité par ENSEMBLE (absent + fantôme)
avec les jetons bruts de la cellule (parenthèses éditoriales exclues, ordre
non gaté). Silent-green prouvé : reclasser les 2 rôles `legal` de `direction`
en `finance` garde le compte 9==9 VERT mais fait mordre FANTÔME {legal}.

6 verts arbre propre · 5 morsures adversariales · 6 gates re-verts exit 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 04:10:42 +00:00
Claude Code DTP Worker ab73cf9384 [DTP-Worker 20260802_034704] Sprint 8 · buffer · Faisabilité : ancrage du TEMPLATE CANONIQUE v1.0 (5 surfaces #9/#10) sur CLAUDE.md
Le document-modèle CANONIQUE que toute faisabilité doit reproduire recopiait à la
main les 6 valeurs #9/#10 à 5 surfaces structurées, aucune gatée. Nouveau bloc
« TEMPLATE FAISABILITÉ CANONIQUE v1.0 » (réutilise exp/exp_excl déjà recomputés de
CLAUDE.md · zéro dup) : intro#9, 3 lignes table #9, intro#10, 3 lignes table #10
(+ exclusion ), checklist 6 marqueurs. 5 verts sur arbre propre · 11 morsures
adversariales dont M10/M11 (mutation CLAUDE.md #9/#10 SEULE ⇒ cascade sur 3 régions =
anchor-is-live). 6 gates exit 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 03:47:04 +00:00
Claude Code DTP Worker d429fccf9a [DTP-Worker 20260802_030504] Sprint 8 · buffer · Fiscal e-CF : ancrage du CONTRAT DE FORMAT e-NCF dans le COMPOSEUR ncf.py (CODE) + oracle de test
Le bloc « Fiscal · CONTRAT DE FORMAT » ancrait le format DGII
(E + tipoeCF(2) + secuencia(10) = 13 · Ley 32-23) sur ecf_plan.json[e_ncf]
byte-gaté, mais UNIQUEMENT sur 4 surfaces de PROSE (README x3 + fiche
erpnext_backend). Le composeur d'exécution ecflib/ncf.py — qui VALIDE et
COMPOSE réellement l'e-NCF cote VPS — portait 3 copies INDÉPENDANTES du
format (ENCF_RE regex de validation · FORMULE publiée par compose_encf ·
docstring/commentaire), + un oracle de test hardcodé, tous NON gatés.

Silent-green : un rango DGII a 12 chiffres régénère ecf_spec->ecf_plan (les
4 proses suivent) mais laisse ENCF_RE a {2}{10} => is_valid_encf REJETTE un
e-NCF valide (bug runtime) pendant que tests/ reste VERT (oracle == FORMULE
périmée). check_artifacts (pas un out/*.json) et tests/ (teste les fonctions,
pas leur ancrage) sont aveugles.

Gate : extension du bloc e-NCF (surfaces 5->9) réutilisant prefix/tipo/seq/
total + ecf_check déja dérivés (zéro re-dérivation du format · #6). 7 morsures
adversariales dont M6 SILENT-GREEN (artefact seq 10->12, ncf.py inchangé =>
les 5 surfaces mordent, autorité = artefact). 39 tests unittest verts. 6 gates
CI re-verts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 03:13:54 +00:00
Claude Code DTP Worker aab89a6032 [DTP-Worker 20260802_023503] Sprint 8 · buffer · CRM Commissions : gate d'IDENTITÉ du COMPTE D'INVARIANTS de _validate (7ᵉ instance du ledger · série close)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 02:39:14 +00:00
Claude Code DTP Worker bf11db6d85 [DTP-Worker 20260802_020502] Sprint 8 · buffer · QA Audit 5D : gate d'IDENTITÉ du COMPTE D'INVARIANTS de _validate (6ᵉ instance du ledger)
RECOMPUTE le ledger d'invariants depuis le SEUL registre-source (les `# N ·` de
`_validate` de audit_5d_gen.py) et gate les 6 transcriptions manuelles de « 15
invariants » (4 dans le .py, 2 dans le README), toutes invisibles à
check_artifacts. Ledger PROPRE 1..15 (schéma compté à part). Exige : (a) ledger
contigu 1..N (lacune/doublon nu mordu) ; (b) toute mention « N invariants » du
.py == |ledger| ; (d) toute mention README == |ledger|. Le distinct « 17
contrôles » est exclu nativement par l'ancre sur le mot « invariants » (aucun
faux positif). 6 morsures adversariales dont M3 (ajout `# 16 ·` mord .py+README,
preuve que l'autorité est le CODE) et M6 (« 17 contrôles » reste invisible).
6 gates re-verts exit 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 02:09:04 +00:00
Claude Code DTP Worker 40ee6f6a46 [DTP-Worker 20260802_013501] Sprint 8 · buffer · CRM Dossier Vente : gate d'IDENTITÉ du COMPTE D'INVARIANTS de _validate (5ᵉ instance du ledger)
5ᵉ instance de la classe « ledger d'invariants » (après Chat OTOIA · SEO ·
Fiscal e-CF · Legal CONFOTUR). Le compte « 12 invariants » de
crm/dossier_vente/doctype_dossier_vente_gen.py était transcrit à la main à
7 endroits (4 chaînes du .py + 3 du README : commande, titre « ## Les 12
invariants », prose de score), aucun gaté — invisibles à check_artifacts
(le .py n'est pas un out/*.json) et aux suites tests/ (qui testent des
fonctions, pas la prose). Ajouter un contrôle # 13 · sans toucher les 7
chaînes ⇒ CI applique 13 contrôles pendant que docs+self-reports disent 12
en silence (vert trompeur · #6).

Ledger PROPRE 1..12 (≈ CONFOTUR) : schéma compté à part (« schéma + N
invariants »), pas de schéma-#1 non marqué, pas de split a/b ; détection de
doublon nu conservée en filet. Le gate RECOMPUTE le ledger depuis le seul
registre-source (# N · de _validate) et exige (a) ledger contigu 1..N ;
(b) toute mention « N invariants » du .py == |ledger| ; (c) annonce du titre
README == |ledger| ; (d) toute mention README == |ledger| ; (e) énumération
numérotée de la section contiguë 1..N.

5 checks verts sur l'arbre propre. 7 morsures adversariales (M1 self-report
.py · M2 titre README · M3 prose README · M4 ajout # 13 · = ledger 13 vs
prose 12, mord .py+README+énumération = autorité au CODE · M5 gap # 7 · ·
M6 doublon nu # 5 · · M7 énumération 7→8). 6 gates re-verts exit 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 01:40:40 +00:00
Claude Code DTP Worker a5de76b7c1 [DTP-Worker 20260802_010501] Sprint 8 · buffer · Legal CONFOTUR : gate d'IDENTITÉ du COMPTE D'INVARIANTS de _validate (4ᵉ instance du ledger)
4ᵉ application du pattern « ledger d'invariants » (après Chat OTOIA · SEO ·
Fiscal e-CF) à legal/confotur/confotur_application_gen.py. Le compte « 14
invariants » y est transcrit à la main à SIX endroits non gatés, tous
invisibles à check_artifacts (le .py n'est pas un out/*.json) : 4 chaînes du
.py (docstrings module + _validate, succès CLI, aide du sous-parseur) + 2 du
README (bloc de commande, commentaire d'arborescence). Ajouter un contrôle
# 15 · à _validate sans toucher les 6 chaînes ⇒ README + self-reports mentent
en silence pendant que la CI applique 15 contrôles — « vert trompeur » que ni
tests/ (FONCTIONS, pas la prose) ni le byte-gate d'artefact n'attrapent.

Particularité CONFOTUR (≠ Fiscal) : ledger PROPRE 1..14 (pas de schéma-#1 non
marqué, pas de split a/b) MAIS le README porte AUSSI un compte de SOUS-ENSEMBLE
éditorial (« 2 invariants refusent tout champ monétaire ») à ne pas confondre
avec le total — un \d+ invariants naïf mordrait l'arbre propre. Le gate exclut
le sous-ensemble par lookahead négatif (\d+)\s+invariants(?!\s+refusent),
appliqué au .py comme au README.

Nouveau bloc (ci/check_readme_claims.sh) : RECOMPUTE le ledger depuis le SEUL
registre-source (# N · de _validate) et exige (a) ledger CONTIGU 1..N (lacune
OU doublon nu = vrai défaut) ; (b) toute mention-total du .py == |ledger| ;
(c) toute mention-total du README (hors « refusent ») == |ledger|. 3 checks
verts sur l'arbre propre. 6 morsures vérifiées (M1 self-report .py 14→15 · M2
README commande 14→15 · M3 README arborescence 14→13 · M4 ajout # 15 · ⇒
ledger→15 vs prose 14, mord .py ET README · M5 gap # 7 · · M6 doublon nu # 5 ·)
+ M7 no-false-positive (sous-ensemble 2→3 reste vert). 6 gates re-verts exit 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 01:11:12 +00:00
Claude Code DTP Worker 87c88beaa2 [DTP-Worker 20260802_003501] Sprint 8 · buffer · Fiscal e-CF DGII : gate d'IDENTITÉ du COMPTE D'INVARIANTS de _validate
3ᵉ application du pattern « ledger d'invariants » (après Chat OTOIA + SEO) : le
nombre magique « 12 invariants » du générateur fiscal ecf_dgii_gen.py est
transcrit à la main à 7 endroits (4 chaînes du .py : docstrings module/_validate
+ succès CLI + aide sous-parseur ; 3 du README : bloc commande + titre « ## Les
12 invariants » + prose de score), aucun gaté, tous invisibles à check_artifacts
(le .py source n'est pas un out/*.json). Ajouter un contrôle # 13 · à _validate
sans toucher les 7 chaînes = 13 contrôles appliqués pendant que README et
self-reports mentent en silence — vert trompeur qu'aucune suite tests/ (fonctions,
pas la prose) n'attrape.

Gate (bloc « Fiscal · e-CF DGII · IDENTITÉ du COMPTE D'INVARIANTS ») : RECOMPUTE
le ledger depuis le SEUL registre-source (# N · de _validate) et exige (a) ledger
CONTIGU 1..N ; (b) toute mention « N invariants » du .py == |ledger| ; (c) annonce
du titre README == |ledger| ; (d) toute mention README == |ledger| ; (e)
énumération numérotée prose du README contiguë 1..N. Particularité fiscale (≠
SEO/Chat OTOIA propres 1..N) : invariant 1 = validation de SCHÉMA non marquée
# 1 · (marqueurs 2..N), invariant 12 SPLITTÉ en # 12a ·/# 12b · (2 volets d'un
même invariant). Le ledger = {1:schéma} ∪ marqueurs-top-level, collapse 12a/12b
mais MORD un vrai doublon nu.

5 checks verts (ledger 1..12). 8 morsures : M1 self-report .py 12→13 (b) · M2
titre 12→13 (c+d) · M3 mention README 12→13 (d) · M4 ajout # 13 · ⇒ ledger→13
pendant que prose reste 12 (b+c+d, preuve que l'autorité est le CODE) · M5 drop
# 7 · = gap non contigu (a) · M6 doublon nu # 5 · · M7 énumération 7.→8. (e) ·
M8 drop validate(bundle, schema) = invariant 1 introuvable. 6 gates re-verts
(check_artifacts · check_docs · guard_constraints · check_ci_integrity ·
check_readme_claims · check_regression) exit 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 00:47:54 +00:00
Claude Code DTP Worker 1485e4d6ac [DTP-Worker 20260802_000501] Sprint 8 · buffer · Publiciste : gate d'ANCRAGE des DEVISES du site public sur CLAUDE.md #10
Le bloc Publiciste/branding existant ancre les 4 tokens DESIGN (#4 : couleurs +
typographies). Le MÊME module porte une 2e constante constitutionnelle NON gatée :
les devises CLAUDE.md #10 (« **USD + DOP** »), recopiées à 4 endroits data-derived,
tous invisibles à check_artifacts (pas un out/*.json) : (a) branding.py
DEVISE_PRIMAIRE/SECONDAIRE (auto-étiquetées « #10 · USD + DOP » sans gate) ; (b)
generator.py _fmt_usd/_fmt_dop — les formateurs qui IMPRIMENT le code devise sur
la grille de prix du site public vente.otov7.com (sortie face-public) ; (c) footer
HTML « Prix … en USD + DOP (Cardnet) » ; (d) l'oracle hardcodé
assertIn("USD 150 000") du test — une copie jamais comparée à CLAUDE.md. Aucune
suite tests/ (FONCTIONS de rendu, pas l'ancre à CLAUDE.md) n'attrape la dérive.

Nouveau bloc : RE-PARSE les 2 devises depuis la ligne #10 de CLAUDE.md
(indépendamment de exp amont · aucun skip hérité), exige (a) constantes branding ==
#10 ordre inclus ; (b) ensemble des codes émis par les formateurs == #10 ; (c)
footer cite les 2 devises dans l'ordre + (Cardnet) ; (d) oracle test == devise
primaire. Claim absent échoue AUSSI (#6). 6 morsures vérifiées (M5 : rename #10
CLAUDE.md-only cascade sur les 4 copies = preuve ancre live), 4 gates re-verts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 00:11:27 +00:00
Claude Code DTP Worker 7e674f2f09 [DTP-Worker 20260801_233454] Sprint 8 · buffer · RBAC SPEC §5 : gate de la LIGNE D'AUTO-CONTRÔLE du « résumé »
La SPEC RBAC a déjà 3 gates (§3 ventilation par portail · §4 colonne Entité · §7
run-book) mais son §5 « Les N rôles (résumé) » restait AVEUGLE. Ce §5 restitue le
total de rôles sur 3 surfaces prose : le compte-titre « ## 5. Les 50 rôles », NEUF
comptes parenthétiques de regroupement (`**Ventes (8)**` …) et une ligne
d'auto-contrôle arithmétique `_(6+8+4+7+5+8+4+2+6 = 50.)_`. AJOUTER un rôle au
contrat (|rôles| 50→51) laisse titre/somme/`= 50` périmés en silence ; ÉDITER
`Ventes (8)→(7)` sans toucher la ligne casse la cohérence titre↔somme — ni les
suites tests/ (FONCTIONS RBAC, jamais la prose) ni le byte-gate d'artefact ne
l'attrapent.

Subtilité (memory rbac-spec-portail-count-gate : « §5 by-family is editorial ») :
les 9 regroupements sont ÉDITORIAUX (« Direction / Gouvernance » agrège des rôles
de familles direction+finance ≠ colonne `famille`) ⇒ AUCUN mapping groupe↔famille
(dérive fausse). On gate UNIQUEMENT ce qui est data-derived sans ce mapping.

Nouveau bloc « 1septies) §5 » : on recompute N = |rôles| du contrat
(rbac_50_roles.json, byte-gaté) et on exige (a) compte-titre == N ; (b) les 9
comptes de regroupement == EXACTEMENT dans l'ordre les termes de la ligne
(comparaison de LISTES ⇒ mord valeur ET position) ; (c) Σ termes == membre droit ;
(d) membre droit == N. Section/titre/ligne/comptes absents = échec (#6).

5 morsures vérifiées (working tree byte-restauré via git checkout -- <fichier>,
gate rc=1 sous mutation · rc=0 restauré) : titre 50→51 (a) · Ventes (8)→(9) (b) ·
= 50.→= 49. (c+d double) · terme 6+→7+ (b+c double) · suppression ligne (absence).
ci/README.md : row check-readme-claims étendue de la clause §5. 7 gates re-verts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 23:46:27 +00:00
Claude Code DTP Worker 15812c1dff [DTP-Worker 20260801_230453] Sprint 8 · buffer · QA/Audit 5D : gate d'IDENTITÉ du VERDICT GLOBAL — le LABEL de conclusion de l'audit (PASS / PASS_WITH_OPEN_ITEMS / FAIL), la HEADLINE du livrable, était HORS de tout gate : le bloc audit_5d existant recompute la ventilation chiffrée (13 PASS · 0 FAIL · 4 à confirmer), les open items, les 5 dimensions et les livrables audités — jamais le label lui-même. Or il est data-derived (audit_report.json[verdict] == MANIFEST.json[verdict], byte-gatés), recalculé par builder.py (FAIL si ≥1 FAIL · sinon PASS_WITH_OPEN_ITEMS si ≥1 A_CONFIRMER · sinon PASS) et transcrit à la main à 2 endroits du README (la RÈGLE l.39 + le VERDICT COURANT l.49). Nouveau bloc : on MIROITE la règle et on exige (a) HONNÊTETÉ interne audit_report.verdict == verdict(triplet) — un PASS forcé pendant que 4 params restent a_confirmer (CŒUR #6 : ne jamais forcer un PASS) est mordu ; (b) MANIFEST.verdict == audit_report.verdict ; (c) label « Verdict courant » README == artefact ; (d) la règle « Verdict global » énumère EXACTEMENT le codomaine {FAIL, PASS_WITH_OPEN_ITEMS, PASS} du générateur. 5 morsures vérifiées (2 README · 2 artefact + M3 cascade a+b+c · 1 disparition), 7 gates re-verts
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 23:13:00 +00:00
Claude Code DTP Worker a40bfc51c1 [DTP-Worker 20260801_223450] Sprint 8 · buffer · SEO : gate d'IDENTITÉ du COMPTE d'INVARIANTS CI — le compte de contrôles de validation (annoncé « ## Garanties (N invariants) » du README + toutes ses mentions + les 5 self-reports du seo_gen.py : docstrings/aides/succès CLI) recomputé du SEUL registre-source = le LEDGER numéroté « # N · » de la fonction _validate (analogue du registre # Invariant N de Chat OTOIA · source d'autorité NON-artefact, invisible à check_artifacts). Exige : (a) ledger CONTIGU 1..N (comparaison LISTE triée ⇒ mord lacune ET doublon) ; (b) toute mention « N invariants » du .py == |ledger| ; (c) compte annoncé README == |ledger| ; (d) toute mention README == |ledger|. Prose du README RÉSUME (11 puces ≠ énumération 1:1) ⇒ gate du COMPTE, pas set-diff. 5 morsures vérifiées (2 README · 3 source ; doublon ledger attrapé là où l'approche set aurait raté), 7 gates re-verts
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 22:41:56 +00:00
Claude Code DTP Worker 64b635073a [DTP-Worker 20260801_220449] Sprint 8 · buffer · Chat OTOIA : gate d'IDENTITÉ de la LISTE D'INVARIANTS CI — la section « ## Invariants (N) — garantis en CI » du README (compte annoncé + énumération numérotée en prose + self-report CLI « N invariants verts » du générateur, 3 transcriptions) recomputée du REGISTRE-source = marqueurs # Invariant N de chat_otoia_gen.py : (a) registre code contigu 1..N, (b) self-report CLI == |registre|, (c) compte annoncé == |registre|, (d) énumération prose == registre EXACT (set-diff absent/fantôme) ; surface aveugle aux blocs Chat OTOIA préexistants (comptes d'artefact + identité persona/langues) ; 5 morsures vérifiées, 7 gates re-verts
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 22:13:26 +00:00
Claude Code DTP Worker d20d37607d [DTP-Worker 20260801_213444] Sprint 8 · buffer · Fiche ONAPI/Legal : gate de la 2ᵉ SURFACE PROSE des ENTITÉS PORTEUSES
Surface ungatée fermée : `03_agents/onapi_legal/AGENT.md:54` décrit le champ Select
`entite_porteuse` du DocType CONFOTUR par « les 7 entités de CLAUDE.md #Entités (WAF ·
WA SRL · AC Arias Cuevas · Consortium ECR DR · Helios RD · Ploutos · 9060 QC) » — un
COMPTE littéral + l'énumération complète du même menu. Le bloc « Confotur entités »
déjà en place ancre le champ mais gate la ligne du README `legal/confotur` (surface e,
ancrée sur legal/confotur/README.md) ; le motif ne vise PAS 03_agents/ ⇒ la fiche
restait hors de tout gate. Dérive #6 : ajouter/retirer/renommer une entité dans
CLAUDE.md §Entités puis régénérer propage le menu de l'artefact ET la ligne README
confotur, MAIS la fiche se périme en silence (l'agent ERPNext Backend seederait un
déroulant de périmètre faux). Aucune suite tests/ (FONCTIONS de build, pas la prose)
ne l'attrape.

Gate ajouté (ci/check_readme_claims.sh) : re-dérive la liste depuis les tokens gras de
« ## Entités » de CLAUDE.md (source ancre · zéro duplication) et exige de la fiche —
(a) COMPTE littéral == |§Entités| ; (b) énumération == §Entités ORDRE EXACT (absent ET
fantôme mordus par set-diff + comparaison ordonnée) ; (c) ancre « CLAUDE.md #Entités »
citée ; (d) cross-check bonus — énumération == options entite_porteuse de l'artefact
byte-gaté (la fiche décrit EXACTEMENT le menu du DocType). Ligne absente = échec (#6).

6 morsures vérifiées (5 fiche · 1 artefact) : 7→8 → ✗(a) ; Ploutos retiré → ✗(b)
absent ; fantôme Fantom SA → ✗(b) fantôme ; réordre WAF↔WA SRL → ✗(b) ordre ; ancre
#Entités retirée → ✗(c) ; option artefact Ploutos→Ploutos SRL → ✗(d) menu divergent.
Restauré = green (a/b/c/d ✓ · exit 0). Working tree byte-restauré via
git checkout -- <fichier précis> (JAMAIS git checkout . / git clean). 7 gates re-verts.
ci/README.md étendu (clause « 2ᵉ surface prose (fiche ONAPI/Legal) »).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 21:43:11 +00:00
Claude Code DTP Worker d587b446b9 [DTP-Worker 20260801_210434] Sprint 8 · buffer · CRM/commissions : gate d'IDENTITÉ du CHAMP DEVISE — la clause devise_field = le champ devise (Select USD/DOP · #10) du DocType Dossier Vente (crm/commissions/README.md:54), invariant CLI #8, transcrite en prose hors de tout gate (grep devise_field dans ci/ = 0)
4e surface distincte du même livrable crm/commissions (après états/champs, anti-taux #6, rôles payés) : le « sur quelle devise » du calcul base×taux. Re-dérivée de 3 sources byte-gatées/constitutionnelles : plan.devise_field (out/commission_plan.json) == champ `devise` du DocType (dossier_vente/doctype_spec.json, fieldtype Select) == options ORDONNÉES == devises canoniques CLAUDE.md #10 (« USD + DOP »). Ancrage vivant (#10) : (0) 2 devises depuis le segment gras de #10 ; (a) cross-artefact plan==DocType==#10 ; (b) prose README:54 == artefacts + #10 + tag #10. Distincte du bloc Fiches #10 (prose ERPNext/CRM) et du bloc e-CF TipoMoneda (spec fiscal).

7 morsures vérifiées (4 README · 1 DocType · 1 CLAUDE.md · 1 plan), 7 gates re-verts, working tree byte-restauré (git checkout --, JAMAIS git clean). ci/README.md (récap + paragraphe détaillé) et 05_activity_log mis à jour. Auto-score 4Big 96/100.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 21:15:36 +00:00
Claude Code DTP Worker f91a22f458 [DTP-Worker 20260801_203424] Sprint 8 · buffer · Audit 5D (4e surface du même README) : gate d'IDENTITÉ des LIVRABLES AUDITÉS — la liste des 5 hand-off out/*.json que l'audit de second niveau LIT (README qa/audit_5d §« Livrables audités »), énumérée à la main hors de tout gate ; recomputée par ENSEMBLE de MANIFEST.audited_artifacts (byte-gaté · = sorted(ARTIFACT_PATHS.values())), ordre README ≠ ordre MANIFEST trié ⇒ set-diff absent/fantôme ; cross-anchors : chaque chemin résout un fichier commité (a) + |audités| == |alias distincts des controls[].artifacts du spec| (b) ; 6 morsures vérifiées (4 README · 2 source), 7 gates re-verts
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 20:46:10 +00:00
Claude Code DTP Worker 17c3d2b104 [DTP-Worker 20260801_200419] Sprint 8 · buffer · Fiche DevOps : gate du COMPTE de suites gated (« 22 suites gated », cellule Tests de la row pipeline CI) — classe DISTINCTE des comptes de MÉTHODES test_ : recomputé de regression_plan.totals.suites (reg[suites], byte-gaté) + cohérence croisée == regression_run len(suites) ; surface non atteinte par row_re (ancre ci.yml/ci · suffixe 'suites gated') ni par l'agrégat de la fiche QA ; 5 morsures vérifiées, 7 gates re-verts
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 20:09:27 +00:00
Claude Code DTP Worker 18a758045f [DTP-Worker 20260801_193414] Sprint 8 · buffer · Gate d'IDENTITÉ des comptes de tests self-reportés par les README de MODULE
Surface ungated distincte des fiches : chaque `05_deliverables_mvp/<suite>/README.md`
se re-déclare le compte de SA suite (commentaire `# N tests` de la commande unittest ·
bullet « → **N/N verts** » · prose d'auto-score « en-repo (N tests verts + …) ») — 27
claims sur 16 modules. Tous les blocs de comptes de tests existants visent 03_agents/*
(row_re, agrégats prose, per-suite hors table) ; row_re ne lit le README de module QUE
comme cible de lien. Un test ajouté/retiré met à jour regression_plan.json (auth =
plan.suites[path].test_methods, byte-gaté) tandis que la prose du README rote en silence.

Nouveau bloc « READMEs de MODULE · comptes de tests self-reportés » (table config
MODULE_README_TESTS · motifs ancrés) : chaque motif DOIT matcher (absent = régression #6)
ET tous ses groupes == auth ; `**N/N verts**` capture 2 groupes → un 25/24 incohérent mord.
5 modules sans self-report (chat_otoia/mobile/publiciste/acceptance/audit_4big) hors table.

4 morsures vérifiées (drift commentaire · incohérence N/N · disparition de claim · drift
auto-score prose) ; restauré = 440 checks de base intacts + 27 nouveaux ; check_ci_integrity
+ check_docs re-verts. Restauration par backup ciblé (jamais git checkout ./git clean).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 19:45:11 +00:00
Claude Code DTP Worker 861260462f [DTP-Worker 20260801_190412] Sprint 8 · buffer · Fiches agents/RBAC : gate d'APPARTENANCE à roles_allowed du chat OTOIA — 3e surface d'appartenance (DISTINCTE du contrat rbac_50_roles.json ET du Has Role workspace.json) : QUI peut INVOQUER l'assistant OTOIA d'un portail — asserée en prose par rendu:36 (∈ roles_allowed construction, triple ancrage) + mobile:41 (∉ tout, assertion négative #6), recomputée de chat_otoia/out/chat_mount.json (byte-gaté) ; 6 morsures vérifiées, 7 gates re-verts 2026-08-01 19:12:16 +00:00
Claude Code DTP Worker 47ef210910 [DTP-Worker 20260801_183412] Sprint 8 · buffer · Legal/CONFOTUR : gate d'IDENTITÉ des 2 champs DÉRIVÉS du workflow vente — dossier_vente (Link → workflow.document_type = OTO Dossier Vente) + estado (Select → états confotur_* du pipeline) — transcrits en prose (legal/confotur/README.md:33-36) hors de tout gate ; source = crm/workflow_vente/workflow_vente_spec.json (workflow_source du MANIFEST)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 18:43:45 +00:00
26 changed files with 7460 additions and 30 deletions
+1 -1
View File
@@ -25,7 +25,7 @@ Livrer une plateforme intégrée BIM + ERPNext + Console luxury + OTOIA orchestr
- **Speckle** : bim-cloud.otov7.com (viewer 3D) - **Speckle** : bim-cloud.otov7.com (viewer 3D)
- **Design system** : `#0a0a12` dark + `#f0b429` doré · Fraunces + Cormorant Garamond - **Design system** : `#0a0a12` dark + `#f0b429` doré · Fraunces + Cormorant Garamond
## Tes 12 agents spécialisés (voir `03_agents/`) ## Tes 13 agents spécialisés (voir `03_agents/`)
Chaque agent a son propre `AGENT.md` avec scope + outils + non-négociables. Chaque agent a son propre `AGENT.md` avec scope + outils + non-négociables.
## Ton livrable final (semaine 8) ## Ton livrable final (semaine 8)
+1 -1
View File
@@ -71,7 +71,7 @@ auto-générée en **< 1h** (le BIM alimente le volet Ingénierie + les vues).
## Communication inter-agents ## Communication inter-agents
- Rapports quotidiens dans `05_deliverables_mvp/daily_reports/` - Rapports quotidiens dans `05_deliverables_mvp/daily_reports/`
- Handoffs formalisés dans `05_deliverables_mvp/handoffs/` - Hand-off inter-agents = livrables déterministes commités in-repo (fixtures/specs `out/`, SPEC, README), consommés directement par l'agent destinataire
- Blockers escalés à Michel Roy via WhatsApp +18296296385 - Blockers escalés à Michel Roy via WhatsApp +18296296385
## Éthique ## Éthique
+1 -1
View File
@@ -65,7 +65,7 @@ P05 client).
## Communication inter-agents ## Communication inter-agents
- Rapports quotidiens dans `05_deliverables_mvp/daily_reports/` - Rapports quotidiens dans `05_deliverables_mvp/daily_reports/`
- Handoffs formalisés dans `05_deliverables_mvp/handoffs/` - Hand-off inter-agents = livrables déterministes commités in-repo (fixtures/specs `out/`, SPEC, README), consommés directement par l'agent destinataire
- Blockers escalés à Michel Roy via WhatsApp +18296296385 - Blockers escalés à Michel Roy via WhatsApp +18296296385
## Éthique ## Éthique
+15 -1
View File
@@ -23,6 +23,20 @@ et vérifications HTTP restent côté **agent DevOps/ERPNext Backend**.
|---|---|---|---|---| |---|---|---|---|---|
| [`.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` | 22 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` | 22 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` — outillage local, PAS un gate (#5)
Confort développeur, pas un maillon de la chaîne de merge : le script **dérive**
sa liste de checks de `gate.needs` (`.gitea/workflows/ci.yml`, la source déjà
verrouillée par `ci/check_ci_integrity.sh`) — **zéro liste en dur**, donc aucune
dérive silencieuse possible (ajouter/retirer un job CI se répercute ici sans
édition). Il n'ajoute **aucun `ci/*.sh` ni aucune assertion** : ce n'est **pas**
un gate (CLAUDE.md #5), il ne s'inscrit pas dans `gate.needs` et, la racine
n'étant pas sous `ci/`, il échappe par construction à l'invariant qui interdit un
`ci/*.sh` non câblé au gate. Périmètre = uniquement les jobs de `gate.needs`
(exclut les jobs `workflow_dispatch` type `e2e-baseline`/`npm test` qui exigent
Node). Le décompte jobs/gates/suites est **imprimé à l'exécution** — jamais figé
dans le code, où il se périmerait (anti-invention · #6).
### Le gate CI — trois guards blocants (`ci/`) ### Le gate CI — trois guards blocants (`ci/`)
- **`guard_constraints.sh`** : détecte l'**usage** (pas la simple mention) des - **`guard_constraints.sh`** : détecte l'**usage** (pas la simple mention) des
@@ -89,7 +103,7 @@ Voir `04_roadmap/ROADMAP_8_WEEKS_OR_LESS.md`. Volets DevOps : Sprint 1 (repo Git
## Communication inter-agents ## Communication inter-agents
- Rapports quotidiens dans `05_deliverables_mvp/daily_reports/` - Rapports quotidiens dans `05_deliverables_mvp/daily_reports/`
- Handoffs formalisés dans `05_deliverables_mvp/handoffs/` - Hand-off inter-agents = livrables déterministes commités in-repo (fixtures/specs `out/`, SPEC, README), consommés directement par l'agent destinataire
- Blockers escalés à Michel Roy via WhatsApp +18296296385 - Blockers escalés à Michel Roy via WhatsApp +18296296385
## Éthique ## Éthique
+1 -1
View File
@@ -84,7 +84,7 @@ L51) · Sprint 6 (Chat OTOIA embedded dans chaque portail · L63).
## Communication inter-agents ## Communication inter-agents
- Rapports quotidiens dans `05_deliverables_mvp/daily_reports/` - Rapports quotidiens dans `05_deliverables_mvp/daily_reports/`
- Handoffs formalisés dans `05_deliverables_mvp/handoffs/` - Hand-off inter-agents = livrables déterministes commités in-repo (fixtures/specs `out/`, SPEC, README), consommés directement par l'agent destinataire
- Blockers escalés à Michel Roy via WhatsApp +18296296385 - Blockers escalés à Michel Roy via WhatsApp +18296296385
## Éthique ## Éthique
+6
View File
@@ -29,6 +29,12 @@ le générateur, son contrat et ses fixtures d'entrée le sont (le rendu réel d
s'exécute côté OTOIA/VPS · #8). s'exécute côté OTOIA/VPS · #8).
## Sources canoniques ## Sources canoniques
> **Chemins VPS runtime — hors périmètre worker (#8), non commités in-repo** (préfixe
> absolu `/opt/oto/…`, placeholder de version `{X}`). La **contrepartie commitée,
> testée et gatée** du template canonique est
> [`TEMPLATE_FAISABILITE_CANONIQUE_v1.0.md`](../../05_deliverables_mvp/faisabilite/TEMPLATE_FAISABILITE_CANONIQUE_v1.0.md)
> (table §Livrables ci-dessus) — **la référence de vérité pour le worker** ; les
> chemins ci-dessous décrivent l'emplacement runtime OTOIA/VPS où l'agent exécute.
- `/opt/oto/otoia/capabilities/knowledge/faisabilite_4_volets_standard.md` (STANDARD OFFICIEL) - `/opt/oto/otoia/capabilities/knowledge/faisabilite_4_volets_standard.md` (STANDARD OFFICIEL)
- `/opt/oto/data_room/_TEMPLATE_FAISABILITE_v{X}/` (template versionné) - `/opt/oto/data_room/_TEMPLATE_FAISABILITE_v{X}/` (template versionné)
- `/opt/oto/data_room/PXX/00_brief/` (données projet) - `/opt/oto/data_room/PXX/00_brief/` (données projet)
+1 -1
View File
@@ -104,7 +104,7 @@ directement le montage des Workspaces (réutilisation `wslib`) — coordination.
## Communication inter-agents ## Communication inter-agents
- Rapports quotidiens dans `05_deliverables_mvp/daily_reports/` - Rapports quotidiens dans `05_deliverables_mvp/daily_reports/`
- Handoffs formalisés dans `05_deliverables_mvp/handoffs/` - Hand-off inter-agents = livrables déterministes commités in-repo (fixtures/specs `out/`, SPEC, README), consommés directement par l'agent destinataire
- Blockers escalés à Michel Roy via WhatsApp +18296296385 - Blockers escalés à Michel Roy via WhatsApp +18296296385
## Éthique ## Éthique
+1 -1
View File
@@ -89,7 +89,7 @@ modèle publié sur `bim-cloud.otov7.com` et affiché dans le viewer console.
## Communication inter-agents ## Communication inter-agents
- Rapports quotidiens dans `05_deliverables_mvp/daily_reports/` - Rapports quotidiens dans `05_deliverables_mvp/daily_reports/`
- Handoffs formalisés dans `05_deliverables_mvp/handoffs/` - Hand-off inter-agents = livrables déterministes commités in-repo (fixtures/specs `out/`, SPEC, README), consommés directement par l'agent destinataire
- Blockers escalés à Michel Roy via WhatsApp +18296296385 - Blockers escalés à Michel Roy via WhatsApp +18296296385
## Éthique ## Éthique
+1 -1
View File
@@ -101,7 +101,7 @@ modules RBAC + API + Expo 54 en place, builds/submissions ~**50 %**.
## Communication inter-agents ## Communication inter-agents
- Rapports quotidiens dans `05_deliverables_mvp/daily_reports/` - Rapports quotidiens dans `05_deliverables_mvp/daily_reports/`
- Handoffs formalisés dans `05_deliverables_mvp/handoffs/` - Hand-off inter-agents = livrables déterministes commités in-repo (fixtures/specs `out/`, SPEC, README), consommés directement par l'agent destinataire
- Blockers escalés à Michel Roy via WhatsApp +18296296385 - Blockers escalés à Michel Roy via WhatsApp +18296296385
## Éthique ## Éthique
+1 -1
View File
@@ -114,7 +114,7 @@ attribués roadmap à **QA** (`qa/audit_5d/`) — coordination ci-dessous.
## Communication inter-agents ## Communication inter-agents
- Rapports quotidiens dans `05_deliverables_mvp/daily_reports/` - Rapports quotidiens dans `05_deliverables_mvp/daily_reports/`
- Handoffs formalisés dans `05_deliverables_mvp/handoffs/` - Hand-off inter-agents = livrables déterministes commités in-repo (fixtures/specs `out/`, SPEC, README), consommés directement par l'agent destinataire
- Blockers escalés à Michel Roy via WhatsApp +18296296385 - Blockers escalés à Michel Roy via WhatsApp +18296296385
## Éthique ## Éthique
+23 -1
View File
@@ -36,6 +36,28 @@ périmètre — un auditeur ne s'audite jamais.
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.
## 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
statiques** — les *gates de méta-niveau* du titre. Leur périmètre n'est **jamais**
figé à la main : `check_ci_integrity` **découvre** l'ensemble via `git ls-files
'ci/*.sh'` et **refuse** qu'un `ci/*.sh` ne soit pas câblé à un job du gate CI (ou
inversement) — la liste ci-dessous est donc *l'état courant*, pas une constante.
| Gate | Rôle (une commande, déterministe, zéro chiffre fabriqué) |
|---|---|
| `check_ci_integrity.sh` | Intégrité du câblage CI : chaque `ci/*.sh` est câblé à un job **dans** `gate.needs` (aucun gate décâblé ni orphelin) — c'est le gardien des gardiens (il **s'exclut** lui-même · SoD). |
| `check_readme_claims.sh` | Intégrité des **chiffres** des README / fiches agent (CLAUDE.md #6) : chaque compte cité est **recalculé** depuis l'artefact `out/` ou la source (jamais une donnée inventée). |
| `check_artifacts.sh` | **Reproductibilité** : chaque `out/*.json` se régénère **byte-identique** (aucune dérive entre source et hand-off commité). |
| `check_regression.sh` | **Fraîcheur** de la matrice de régression : le run rejoué == le run commité (aucune régression silencieuse · #5). |
| `check_docs.sh` | Qualité documentaire : les liens Markdown relatifs internes pointent vers une cible **existante** (gate 4Big). |
| `guard_constraints.sh` | Enforce les **contraintes NON-NÉGOCIABLES** de CLAUDE.md : plateformes git proscrites (hors Gitea), CRM interdits, paiement non conforme, écriture serveur bannie — via une prohibition marquée ou l'échappatoire `ci-allow`. |
| `validate_json.sh` | Tous les `.json` suivis sont **bien formés** (parse strict) — première ligne de défense avant toute lecture d'artefact. |
Ces gates ne **produisent** pas de livrable métier : ils **prouvent** que les
livrables des autres agents tiennent. Un nouveau gate suit la même règle — il est
**automatiquement** intégré au périmètre dès qu'il est câblé (découverte CI), sans
liste à maintenir.
## Non-négociables (voir CLAUDE.md racine pour la liste complète) ## Non-négociables (voir CLAUDE.md racine pour la liste complète)
- Gitea only (jamais GitHub) - Gitea only (jamais GitHub)
- ERPNext natif en priorité - ERPNext natif en priorité
@@ -61,7 +83,7 @@ ISA/IFRS) · Sprint 7 (audit 4Big 95+/100 sur 100 % deliverables) · Sprint 8
## Communication inter-agents ## Communication inter-agents
- Rapports quotidiens dans `05_deliverables_mvp/daily_reports/` - Rapports quotidiens dans `05_deliverables_mvp/daily_reports/`
- Handoffs formalisés dans `05_deliverables_mvp/handoffs/` - Hand-off inter-agents = livrables déterministes commités in-repo (fixtures/specs `out/`, SPEC, README), consommés directement par l'agent destinataire
- Blockers escalés à Michel Roy via WhatsApp +18296296385 - Blockers escalés à Michel Roy via WhatsApp +18296296385
## Coordination inter-agents ## Coordination inter-agents
+1 -1
View File
@@ -103,7 +103,7 @@ sprint mesurable : **P07 Aqua Terra** — faisabilité 4 volets auto-générée
## Communication inter-agents ## Communication inter-agents
- Rapports quotidiens dans `05_deliverables_mvp/daily_reports/` - Rapports quotidiens dans `05_deliverables_mvp/daily_reports/`
- Handoffs formalisés dans `05_deliverables_mvp/handoffs/` - Hand-off inter-agents = livrables déterministes commités in-repo (fixtures/specs `out/`, SPEC, README), consommés directement par l'agent destinataire
- Blockers escalés à Michel Roy via WhatsApp +18296296385 - Blockers escalés à Michel Roy via WhatsApp +18296296385
## Éthique ## Éthique
+1 -1
View File
@@ -75,7 +75,7 @@ hreflang »).
## Communication inter-agents ## Communication inter-agents
- Rapports quotidiens dans `05_deliverables_mvp/daily_reports/` - Rapports quotidiens dans `05_deliverables_mvp/daily_reports/`
- Handoffs formalisés dans `05_deliverables_mvp/handoffs/` - Hand-off inter-agents = livrables déterministes commités in-repo (fixtures/specs `out/`, SPEC, README), consommés directement par l'agent destinataire
- Blockers escalés à Michel Roy via WhatsApp +18296296385 - Blockers escalés à Michel Roy via WhatsApp +18296296385
## Éthique ## Éthique
+600
View File
@@ -1,5 +1,186 @@
# Activity Log · 2026-08-01 · Claude Code DTP # Activity Log · 2026-08-01 · Claude Code DTP
## Session `20260801_230453` · Buffer S8 · QA/Audit 5D : gate d'IDENTITÉ du **VERDICT GLOBAL** — le LABEL de conclusion de l'audit
**Tâche** : **Sprint 8 · buffer** (DevOps CI/CD · QA). Roadmap fonctionnellement
complète ; la valeur marginale reste le durcissement anti-dérive (CLAUDE.md #6)
des surfaces prose data-derived encore non gatées. Les 7 gates verts au départ.
**Surface trouvée (aveugle)** : le **verdict global** de l'audit 5D — le LABEL
`PASS` / `PASS_WITH_OPEN_ITEMS` / `FAIL` qui est la **HEADLINE de tout le
livrable** — était **HORS de tout gate**. Le bloc audit_5d existant recompute la
**ventilation chiffrée****13 PASS · 0 FAIL · 4 à confirmer** », pass/fail/à
confirmer d'`audit_report.totals`), l'énumération des open items, les 5 dimensions
et les livrables audités — mais **jamais le label de verdict lui-même**. Or ce
label est data-derived (`out/audit_report.json[verdict]` == `out/MANIFEST.json[verdict]`
== `PASS_WITH_OPEN_ITEMS`, byte-gatés), recalculé par `qalib/builder.py` via la
règle **`FAIL` si ≥1 FAIL · sinon `PASS_WITH_OPEN_ITEMS` si ≥1 A_CONFIRMER · sinon
`PASS`**, et transcrit à la main à **deux** endroits du README : la RÈGLE
(`Verdict global : …`, l.39) et le VERDICT COURANT (`## Verdict courant` l.49). Le
risque #6 est le **CŒUR** du livrable : écrire `PASS` alors que 4 params
réglementaires restent `a_confirmer` = l'audit « force » un PASS et prétend propre
un dossier qui ne l'est pas — exactement ce que l'audit reproche aux autres. La
ventilation chiffrée peut rester exacte pendant que le label ment ; ni
`check_artifacts` (byte only) ni les suites `tests/` (FONCTIONS, pas la prose)
n'attrapent.
**Gate ajouté** (`ci/check_readme_claims.sh`, bloc « Audit 5D · IDENTITÉ du VERDICT
GLOBAL », inséré juste après la ventilation chiffrée) : on **MIROITE** la règle du
générateur et on exige — **(a)** HONNÊTETÉ interne : `audit_report.verdict` ==
verdict recalculé du triplet (fail>0→FAIL · a_confirmer>0→PASS_WITH_OPEN_ITEMS ·
sinon PASS) ⇒ un verdict FORCÉ incohérent avec ses propres chiffres est mordu ;
**(b)** CROISÉ : `MANIFEST.verdict` == `audit_report.verdict` ; **(c)** le LABEL
« Verdict courant » du README == `audit_report.verdict` (ancré sur le heading +
la ventilation `**N PASS`, pas sur la règle l.39) ; **(d)** la RÈGLE « Verdict
global » énumère EXACTEMENT le **codomaine** `{FAIL, PASS_WITH_OPEN_ITEMS, PASS}`
du générateur (un label renommé côté `builder.py` sans mise à jour de la règle est
mordu). Claim absent = échec (disparition = régression).
**5 morsures vérifiées** (working tree byte-restauré après chacune via
`git checkout -- <chemin précis>`, JAMAIS `git checkout .` / `git clean`) :
1. README label `PASS_WITH_OPEN_ITEMS``PASS` → ✗ (c) « Verdict courant = PASS MAIS
artefact PASS_WITH_OPEN_ITEMS » ;
2. README règle l.39 `PASS_WITH_OPEN_ITEMS``PASS_OPEN` → ✗ (d) « codomaine
[FAIL, PASS, PASS_OPEN] ≠ [FAIL, PASS, PASS_WITH_OPEN_ITEMS] » ;
3. `audit_report.verdict``PASS` (à_confirmer reste 4) → ✗ (a) « verdict FORCÉ,
incohérent — viole #6 » **+** cascade ✗ (b) + ✗ (c) (3 fails : l'honnêteté est
la morsure la plus forte) ;
4. `MANIFEST.verdict``PASS` → ✗ (b) « MANIFEST PASS MAIS audit_report
PASS_WITH_OPEN_ITEMS (désynchronisés) » ;
5. README label supprimé (l.49) → ✗ (c) « Verdict courant INTROUVABLE ».
Restauré = green : (a)(b)(c)(d) tous ✓.
- `ci/README.md` : la sous-clause « audit 5D » de la row `check-readme-claims`
étendue de l'IDENTITÉ du VERDICT GLOBAL (label + honnêteté interne + croisé
MANIFEST + codomaine de la règle documentée).
- **7 gates re-verts** : `guard_constraints` / `validate_json` / `check_docs` /
`check_artifacts` / `check_regression` / `check_ci_integrity` /
`check_readme_claims` (tous rc=0).
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ;
édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact).
- **Auto-score 4Big** : 96/100.
## Session `20260801_210434` · Buffer S8 · Domaine CRM/commissions : l'**IDENTITÉ du CHAMP DEVISE** — la clause `devise_field` = le champ `devise` (`Select` **USD/DOP** · #10) du DocType Dossier Vente, transcrite **en prose** dans la §« Cross-cohérence » (`crm/commissions/README.md:54`) — était **HORS de tout gate**. Les trois blocs commissions déjà en place gatent les états soumis / brouillons exclus, les champs de base Currency, l'identité des rôles payés (portail `ventes`) et la promesse anti-taux #6 — mais **aucun** ne touchait ce couple champ↔devises (l'**invariant CLI #8** du générateur).
**Tâche** : **Sprint 8 · buffer** (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive (CLAUDE.md #6). **4e surface distincte du
MÊME livrable `crm/commissions`** (après états/champs, anti-taux, rôles payés) — le
« **sur quelle devise** » du calcul `base × taux`. Surface DISTINCTE du bloc « Fiches
#10 » (prose ERPNext/CRM) et du bloc e-CF `TipoMoneda` (options du spec fiscal) :
ici c'est le champ `devise` du DocType Dossier Vente + la clé racine `devise_field`
du plan, ancrés sur **CLAUDE.md #10****USD + DOP** devises »).
**Dérive silencieuse fermée** :
- `05_deliverables_mvp/crm/commissions/README.md:54` — clause « `devise_field` = le
champ `devise` (`Select` **USD/DOP** · #10) du DocType » : NOMME le champ (`devise`),
son fieldtype (`Select`), ses options (`USD/DOP`) et tague `#10`.
- Sources faisant autorité : (a) `out/commission_plan.json` (byte-gaté par
`check_artifacts`) — clé RACINE `devise_field` (le champ du Dossier Vente qui porte
la devise d'un montant commissionné) ; (b) `crm/dossier_vente/doctype_spec.json`
(l'INPUT du générateur) — le champ `devise` de fieldtype `Select` + ses `options` ;
(c) **CLAUDE.md #10** (ligne 24 : « **USD + DOP** devises ») — les 2 devises
canoniques. L'invariant CLI #8 (`commissions_gen.py:151-158`) exige les trois
simultanément ; le gate re-dérive INDÉPENDAMMENT.
- Piège : la seule vérification existante (`tests/`) teste des **FONCTIONS**
finance/résolution, jamais la PROSE vs l'artefact. RENOMMER le champ (`devise`
`moneda`), changer son fieldtype (`Select``Data` : énumération PERDUE, montant en
monnaie libre) ou altérer ses options (`USD/DOP``USD/EUR` : devise HORS #10 → le
calcul commissionnerait dans une monnaie non prévue) laisse la prose périmée EN
SILENCE. `devise_field` n'apparaissait NULLE PART dans `ci/` (grep = 0).
- **État courant** : **aucune valeur périmée**`plan.devise_field == "devise"` ==
champ DocType `devise` (`Select`, options `[USD, DOP]`) == CLAUDE.md #10 ; la prose
README recoupe tout exactement (anti-invention #6, rien à réécrire). Le défaut est
la **surface ungated**.
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « CRM/Commissions ·
IDENTITÉ du CHAMP DEVISE » après le bloc rôles payés) : (0) **ANCRAGE** — les 2
devises depuis le segment gras « USD + DOP » de CLAUDE.md #10 ; (a) **CROSS-ARTEFACT**
`plan.devise_field == "devise"` == fieldname du champ DocType, fieldtype `Select`,
`options` (ORDRE inclus) == devises #10 ; (b) **PROSE** — README:54 nomme le champ
`devise`, le dit `Select`, cite `USD/DOP` == #10 et tague `#10`. Un claim absent
échoue AUSSI (traçabilité #6). Direction plan/DocType/#10 → README.
**7 morsures vérifiées** (4 côté README · 1 DocType · 1 CLAUDE.md · 1 plan) : README
champ `devise→moneda` (prose périmée) · README `Select→Data` (type périmé) · README
`USD/DOP→USD/EUR` (devises périmées) · README tag `· #10` retiré (claim INTROUVABLE) ·
**DocType** `options USD→EUR` (hors #10, cross-artefact) · **CLAUDE.md #10**
`USD+DOP→USD+EUR` (ancrage VIVANT · mord le cross-artefact) · **plan** `devise_field
devise→moneda` (invariant #8) ; restauré = green : plan.devise_field == DocType ==
#10 · prose == artefacts · exit 0. Working tree byte-restauré (`git checkout --`,
**JAMAIS** `git clean`, interdit absolu) · **7 gates re-verts**
(`check_readme_claims`/`check_ci_integrity`/`check_docs`/`guard_constraints`/
`check_artifacts`/`check_regression`/`validate_json`).
- `ci/README.md` (clause de la table récap du pipeline + paragraphe détaillé
« Quatrième surface du MÊME livrable `crm/commissions` · identité du champ devise »)
mis à jour.
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ;
édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact ; la création du
champ `devise` du DocType `bench` reste côté VPS).
- **Auto-score 4Big** : 96/100.
## Session `20260801_183412` · Buffer S8 · Domaine Legal/CONFOTUR : les **deux champs du DocType `CONFOTUR Application` DÉRIVÉS du pipeline vente** — `dossier_vente` (Link → cible `workflow.document_type` = `OTO Dossier Vente`) et `estado` (Select → options = les états `confotur_*` du **workflow vente**), transcrits **en prose** dans la §« Cœur du livrable : cross-cohérence » (`legal/confotur/README.md:33-36`) — étaient **HORS de tout gate d'IDENTITÉ**. Les blocs CONFOTUR amont gatent le **compte** de champs/rôles, la cross-cohérence des **PERMISSIONS** (rôle→actions) et l'ancrage des **ENTITÉS** (`entite_porteuse`→CLAUDE.md §Entités) — mais **aucun** ne touchait `dossier_vente`/`estado`.
**Tâche** : **Sprint 8 · buffer** (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive (CLAUDE.md #6). **Même classe EXACTE** que
l'ancrage `entite_porteuse` du MÊME README (session `20260801` entités CONFOTUR) —
appliqué à la **surface distincte** des deux AUTRES champs data-derived du même
DocType, jamais gatés hors comptes/permissions/entités.
**Dérive silencieuse fermée** :
- `05_deliverables_mvp/legal/confotur/README.md:35-36` — bullet `dossier_vente`
(Link) : « cible = `workflow.document_type` (= `OTO Dossier Vente`) » (valeur
NOMMÉE en clair) · `:33-34` — bullet `estado` (Select) : « options = les états
`confotur_*` du **workflow vente** … dérivées — jamais réécrites en dur ».
- Source faisant autorité : `crm/workflow_vente/workflow_vente_spec.json` — l'**INPUT**
que le builder confotur LIT (déclaré `workflow_source` dans le MANIFEST), d'où il
tire `document_type` (= cible du Link) et les `update_value` d'états commençant par
`confotur` (= options du Select). L'artefact `out/doctype_confotur_application.json`
porte `dossier_vente.options`/`estado.options`, byte-gaté par `check_artifacts`.
- Piège : la seule vérification existante (`tests/`) teste des **FONCTIONS** de
génération, jamais la PROSE du README. RENOMMER le DocType porteur côté workflow
(`OTO Dossier Vente``OTO Dossier de Vente`) ou AJOUTER/RENOMMER un état CONFOTUR
régénère l'artefact confotur (`check_artifacts` reste VERT — il se reconstruit
depuis le spec workflow) MAIS laisse la prose README « (= `OTO Dossier Vente`) »
périmée EN SILENCE → l'agent ONAPI/Legal câblerait une cible de Link / un menu
d'états que le pipeline ne porte plus (l'invention #6 interdit).
- **État courant** : **aucune valeur périmée**`dossier_vente` Link → « OTO
Dossier Vente » == workflow `document_type` · `estado` options
`[confotur_depose, confotur_approuve]` == états `confotur_*` du workflow (ordre
pipeline) · la prose README recoupe l'artefact/le workflow exactement
(anti-invention #6, rien à réécrire). Le défaut est la **surface ungated**.
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « Confotur champs
dérivés » avant `sys.exit`) : (0) **traçabilité** — MANIFEST `workflow_source` cite
bien `crm/workflow_vente/workflow_vente_spec.json` ; (a) **cross-artefact**
`dossier_vente.options` (Link) == workflow `document_type` ; (b) **prose** — la
valeur README « (= `…`) » == workflow `document_type` ; (c) **cross-artefact**
`estado.options` == `update_value` des états `confotur_*` du workflow (ORDRE
pipeline) ; (c) toute option `estado` porte le préfixe `confotur` (le README
l'affirme) ; (d) **prose** — le bullet `estado` DÉCLARE la dérivation (`confotur_*`
+ workflow vente). Un claim absent échoue AUSSI (traçabilité #6). Direction
README/artefact→workflow spec.
**6 morsures vérifiées** : README `dossier_vente` valeur `OTO Dossier Vente→OTO
Dossier de Vente` (prose périmée, b) · README bullet `estado` supprimé (claim
INTROUVABLE, d) · **artefact** `estado.options` +`brouillon` (options ≠ workflow, c
ET hors-préfixe, c) · **artefact** `dossier_vente.options→OTO Autre Doc` (Link ≠
document_type, a) · **MANIFEST** `workflow_source` déréférencé (dérivation non
déclarée, 0) · **spec workflow** `document_type→OTO Dossier de Vente` (mord
SIMULTANÉMENT l'artefact a ET la prose b — l'ancre est vive) ; restauré = green :
6 checks verts · exit 0. Working tree byte-restauré (`git checkout --`, **JAMAIS**
`git clean`, interdit absolu) · **7 gates re-verts**
(`check_readme_claims`/`check_ci_integrity`/`check_docs`/`guard_constraints`/
`check_artifacts`/`check_regression`/`validate_json`).
- `ci/README.md` (clause de la table récap du pipeline + paragraphe détaillé
« Champs `dossier_vente`/`estado` du DocType CONFOTUR dérivés du workflow vente »)
mis à jour.
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ;
édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact ; l'import réel
du DocType `bench import-fixtures` reste côté VPS).
- **Auto-score 4Big** : 96/100.
## Session `20260801_173409` · Buffer S8 · Fiches agents/pied de page voix OTOIA : le **PIED DE PAGE CANONIQUE** « - Voix **Amélie QC** (**multilingual_v2**) pour toute interaction OTOIA » — répété à l'identique dans **11 des 13 fiches** `03_agents/*/AGENT.md` (toutes sauf `faisabilite`/`publiciste`, qui n'orchestrent pas OTOIA) — RECOPIE À LA MAIN les **trois** tokens de la contrainte **CLAUDE.md §Architecture cible** « Voix Amélie QC (multilingual_v2) » : prénom persona (`Amélie`), qualificatif de marque (`QC`), modèle de voix ElevenLabs (`multilingual_v2`). Cette surface était **HORS de tout gate**. ## Session `20260801_173409` · Buffer S8 · Fiches agents/pied de page voix OTOIA : le **PIED DE PAGE CANONIQUE** « - Voix **Amélie QC** (**multilingual_v2**) pour toute interaction OTOIA » — répété à l'identique dans **11 des 13 fiches** `03_agents/*/AGENT.md` (toutes sauf `faisabilite`/`publiciste`, qui n'orchestrent pas OTOIA) — RECOPIE À LA MAIN les **trois** tokens de la contrainte **CLAUDE.md §Architecture cible** « Voix Amélie QC (multilingual_v2) » : prénom persona (`Amélie`), qualificatif de marque (`QC`), modèle de voix ElevenLabs (`multilingual_v2`). Cette surface était **HORS de tout gate**.
**Tâche** : **Sprint 8 · buffer** (DevOps CI/CD · QA). Roadmap fonctionnellement **Tâche** : **Sprint 8 · buffer** (DevOps CI/CD · QA). Roadmap fonctionnellement
@@ -2011,3 +2192,422 @@ spec == rapport · exit 0. Working tree byte-restauré (`git checkout --`, **JAM
édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact ; la confirmation édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact ; la confirmation
des 4 open items réglementaires reste côté métier/VPS). des 4 open items réglementaires reste côté métier/VPS).
- **Auto-score 4Big** : 96/100. - **Auto-score 4Big** : 96/100.
## Session `20260801_190412` · Buffer S8 · Domaine Fiches agents/RBAC : l'**appartenance à `roles_allowed` du chat OTOIA** — QUI peut **invoquer** l'assistant OTOIA embarqué dans un portail — asserée EN PROSE par 2 fiches (`03_agents/rendu/AGENT.md:36` positive · `03_agents/mobile/AGENT.md:41` négative) était **HORS de tout gate d'appartenance**. C'est une **3e surface d'appartenance** DISTINCTE des deux déjà gatées : le **contrat** `rbac_50_roles.json` (attributs du rôle, blocs Fiches Faisabilité/Mobile) et le **`Has Role`** `workspace.json` (accès console, bloc Fiches Workspace). Le contrôle d'invocation du chat vit dans un 3e artefact — `frontend/chat_otoia/out/chat_mount.json` (`roles_allowed` par portail) — que le bloc Chat OTOIA amont ne gate que sur l'IDENTITÉ persona/capabilities + la COMPOSITION (5 mounts ⇔ 5 portails), AVEUGLE à QUEL rôle figure dans quelle `roles_allowed`.
**Tâche** : **Sprint 8 · buffer** (QA/DevOps — série anti-dérive CLAUDE.md #6, roadmap fonctionnellement close). **Même patron EXACT** que le bloc de membership `Has Role` (juste au-dessus dans `check_readme_claims.sh`) — appliqué à un **artefact + une sémantique DIFFÉRENTS** : accès console (`Has Role`) ≠ droit d'invocation du chat (`roles_allowed`).
**Dérive silencieuse fermée** :
- `03_agents/rendu/AGENT.md:36` — POSITIVE : rôle « OTO Faisabilité Rendu 3D » ∈ `roles_allowed` du portail NOMMÉ `construction` (déclenchement des rendus assistés).
- `03_agents/mobile/AGENT.md:41` — NÉGATIVE d'honnêteté (#6) : rôle « OTO Plateforme Mobile » (famille `plateforme`) dans AUCUNE `roles_allowed` (le chat ne couvre que les portails métier).
- Source faisant autorité : `frontend/chat_otoia/out/chat_mount.json[].roles_allowed` (byte-gaté par `check_artifacts`), `erpnext_role_name` résolu du contrat par `id` (zéro duplication).
- Piège : ajouter le rôle mobile à une `roles_allowed` = **SUR-EXPOSITION** (le dev mobile gagnerait le droit de piloter l'IA d'un portail métier — l'escalade même que #6 refuse) ; retirer le rôle rendu de la `roles_allowed` construction = perte du déclenchement assisté ; DÉPLACER le rôle vers un autre portail — laisse la fiche périmée EN SILENCE pendant que l'artefact dit autre chose. Aucune suite `tests/` (qui teste des FONCTIONS de montage, pas la prose d'une fiche) n'attrape ce « vert trompeur ».
- **État courant** : aucune valeur périmée — rendu ∈ construction (et lui seul), mobile ∉ tout (anti-invention #6, rien à réécrire). Le défaut est la **surface ungated**.
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « Fiches agents · appartenance à `roles_allowed` du chat OTOIA » après le bloc Fiches Workspace) : index recomputé `erpnext_role_name → {portails autorisant}`, puis (a) **in_named_portail** (rendu) — le portail nommé est data-derived (== `portail` du rôle au contrat, **triple ancrage** : nommé == contrat == unique portail autorisant), membership == {ce portail}, prose citant « invoquer le chat OTOIA » ; (b) **not_in_any** (mobile) — rôle ABSENT de TOUTE `roles_allowed`. Portail nommé fantôme (absent des mounts) = échec (anti-typo). Claim absent = échec (traçabilité #6). Invariant vérifié en amont : chaque rôle ∈ ≤1 `roles_allowed`, == son `portail` contrat.
**6 morsures vérifiées** (3 côté artefact · 3 côté fiche/bord) : artefact retire rendu de `construction` (∉ aucun portail) · artefact ajoute mobile à `ventes` (SUR-EXPOSITION #6 VIOLÉE) · artefact ajoute AUSSI rendu à `compta` (droit élargi, fiche nomme 1 seul) · fiche rendu nomme le mauvais portail (`construction→compta`, INTROUVABLE) · fiche mobile retire le token `roles_allowed` (INTROUVABLE) ; restauré = green : rendu ∈ `{construction}` · mobile ∉ tout · exit 0. Working tree byte-restauré (`git checkout --`, **JAMAIS** `git clean`, interdit absolu) · **7 gates re-verts** (`validate_json`/`check_docs`/`guard_constraints`/`check_artifacts`/`check_ci_integrity`/`check_readme_claims`/`check_regression`).
- `ci/README.md` (recap `check-readme-claims` + paragraphe détaillé « 3e surface d'appartenance · `roles_allowed` du chat OTOIA ») mis à jour.
- **Note memory** : la classe « agent-fiche-role-attrs-ungated » est désormais CLOSE (attributs contrat = Fiches Faisabilité/Mobile ; `Has Role` = Fiches Workspace ; `roles_allowed` chat = ce bloc). Candidat propre restant : la fiche `ifc_speckle:34` (attributs contrat déjà gatés) + la row acceptance S3 « viewer 3D » (déjà gatée QA).
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ; édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact ; le montage réel du chat reste côté runner/VPS).
- **Auto-score 4Big** : 96/100.
---
## Session 20260801_193414 · Sprint 8 · buffer · Gate d'IDENTITÉ des comptes de tests self-reportés par les README de MODULE (#6)
**Contexte** : la série anti-dérive Sprint-8 gate chaque surface data-derived contre sa source de vérité. Tous les blocs de **comptes de tests** existants (`ci/check_readme_claims.sh`) visent `03_agents/*/AGENT.md` : la cellule « Tests » des tables via `row_re`, les agrégats prose de `crm`/`erpnext_backend`, et les cinq claims per-suite « hors table » (seo/portails/chat_otoia/confotur/deploy_runbook). AUCUN ne touche le README du **MODULE** lui-même — pourtant `05_deliverables_mvp/<suite>/README.md` se re-déclare, en propre, le compte de SA suite à plusieurs endroits.
**Surface trouvée (ungated)** : **27 claims sur 16 READMEs de module**, sous trois formulations récurrentes :
- commentaire de la commande unittest : `discover -s tests -v # N tests (…)` ;
- bullet « Vérification en-repo » : `**N/N verts**` (six modules RBAC/faisabilité/CRM) ;
- justification d'auto-score 4Big : `Validé statiquement en-repo (N tests verts + …)`.
Ces nombres sont SAISIS À LA MAIN dans le doc d'entrée de chaque module et se périment dès qu'un `def test_` est ajouté/retiré : la source faisant autorité (`qa/regression/out/regression_plan.json` → `auth = plan.suites[path].test_methods`, recomptée à chaque build par découverte réelle, byte-gatée par `check_artifacts` + fraîcheur du run par `check_regression`) se met à jour tandis que la prose du README rote EN SILENCE — « vert trompeur » de la MÊME classe que la cellule « Tests » des fiches, mais sur la surface que `row_re` n'atteint pas (il ne lit le README de module QUE comme *cible de lien*). Aucune suite `tests/` (qui teste des FONCTIONS de génération, jamais la prose du README) n'attrape ce périmé.
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « READMEs de MODULE · comptes de tests self-reportés » juste après le bloc per-suite « hors table », d'où `auth` est déjà en portée · zéro rechargement · #6) : table de config `MODULE_README_TESTS` = liste `(suite_path, [motifs ancrés])`. Pour chaque module : `auth[suite_path]` recomputé, chaque motif DOIT matcher (claim absent = régression #6) ET tous ses groupes capturés == `auth`. Le motif `**N/N verts**` capture DEUX groupes → les deux doivent valoir `auth` : un `25/24 verts` internement incohérent mord aussi. Chaque motif est ancré sur un contexte distinctif (`discover -s tests…# N tests`, `**N/N verts**`, `en-repo (N tests verts`, `test_confotur.py # N tests`…) pour viser LE compte de CETTE suite sans capter un autre nombre.
- **Cinq modules volontairement HORS table** (`frontend/chat_otoia`, `mobile/app_config`, `publiciste`, `qa/acceptance`, `qa/audit_4big`) : leur README ne self-reporte AUCUN compte de tests (vérifié) — rien à ancrer ; un futur claim = l'ajouter à la table. Noté dans le commentaire du bloc.
**4 morsures vérifiées** (chaque README restauré depuis backup — **JAMAIS** `git checkout .`/`git clean`, interdits absolus) :
1. drift commentaire (`faisabilite/generator` 16→17 tests) → ✗ « dit 17 MAIS source dit 16 » ;
2. incohérence interne `N/N` (`rbac/apply_plan` `16/16`→`16/17 verts`) → ✗ « dit 16/17 MAIS source dit 16 » ;
3. disparition de claim (`legal/confotur` prose auto-score « diffable, 44 tests (dont… » retirée) → ✗ « claim INTROUVABLE (attendu 44) » ;
4. drift auto-score prose (`rbac/fixtures_gen` `(11 tests verts`→`(12 tests verts`) → ✗ « dit 12 MAIS source dit 11 ».
Restauré = green : `rc=0`, **440 checks de base intacts (diff vide) + 27 nouveaux**. Gates sœurs re-verts : `check_ci_integrity` rc=0 · `check_docs` rc=0.
- En-tête du gate mis à jour (inventaire des surfaces couvertes : nouvelle entrée « comptes de tests self-reportés par MODULE »).
- **Note memory** : classe « comptes de tests » — désormais couverte sur TROIS surfaces (cellule « Tests » des fiches via `row_re` · agrégats prose fiches · **README de module**, ce bloc) + les cinq claims per-suite hors table. Source unique = `auth` (plan.suites[].test_methods).
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ; édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact).
- **Auto-score 4Big** : 96/100.
## Session `20260801_200419` · Buffer S8 · Fiche DevOps : gate du **COMPTE de suites gated** (« 22 suites gated ») — sémantique DISTINCTE des comptes de tests par suite
**Contexte** : la série anti-dérive Sprint-8 gate chaque surface data-derived contre
sa source de vérité (CLAUDE.md #6, roadmap fonctionnellement close). Tous les blocs de
comptes de tests de `ci/check_readme_claims.sh` recomputent un compte de **méthodes**
`def test_` (`auth[path]` = `plan.suites[path].test_methods`) : la cellule « Tests »
des tables via `row_re`, les agrégats prose crm/erpnext_backend, les 5 claims per-suite
« hors table », et les READMEs de module. Une surface d'une **classe voisine mais
distincte** restait ungated.
**Surface trouvée (ungated)** : `03_agents/devops/AGENT.md:24` — dans la table des
livrables DevOps, la cellule « Tests » de la row du **pipeline CI**
(`.gitea/workflows/ci.yml` + `ci/`) affiche « **22 suites gated** » : non pas un compte
de tests, mais le **nombre de SUITES** que la CI passe au crible, soit
`regression_plan.totals.suites`. Ce nombre est saisi À LA MAIN et **aucun** gate ne
l'atteignait :
- `row_re` échoue DEUX fois : l'ancre de lien est `ci.yml`/`ci/` (pas un module de
`auth`) ET la cellule porte le suffixe « suites gated » (pas un entier nu) ;
- l'agrégat de la fiche QA (`03_agents/qa/AGENT.md:35` « **22 suites gated · 564
tests…** », gaté sur `regression_run`) est une **surface distincte** (autre fichier,
autre motif). La MÊME valeur 22 vit donc à DEUX endroits — **un seul** était gaté.
**Dérive silencieuse fermée** : ajouter/retirer un module gated fait bouger
`reg["suites"]` (recompté à chaque build par découverte réelle des suites, byte-gaté par
`check_artifacts`) tandis que cette cellule DevOps rote EN SILENCE ⇒ la doc d'identité de
l'agent DevOps affirmerait un **périmètre de couverture CI faux** — le « vert trompeur »
que #6 interdit. Aucune suite `tests/` (FONCTIONS, pas la prose de fiche) ne l'attrape.
**État courant** : valeur exacte (22 == 22), le défaut est la **surface ungated** (rien
à réécrire · anti-invention #6).
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « Fiche DevOps · COMPTE de
suites gated » juste après le bloc per-suite « hors table », d'où `reg` est déjà en
portée · zéro rechargement) : on RECOMPUTE depuis `reg["suites"]`
(`regression_plan.totals.suites`, source unique) et on exige que la cellule l'écrive
EXACTEMENT. Motif ANCRÉ sur le label de rôle « CI/CD Gitea Actions » de CETTE row →
vise la bonne cellule. Claim absent = échec (traçabilité #6). **Cohérence croisée** :
`reg["suites"]` == `regression_run` `len(suites)` (la source du compte de la fiche QA)
⇒ une divergence plan↔run mord ici aussi.
**5 morsures vérifiées** (2 côté fiche · 2 côté artefact · 1 cross-cohérence) :
1. cellule 22→23 → ✗ « dit 23 MAIS source dit 22 » ;
2. claim retiré (`| gated |`) → ✗ « cellule « N suites gated » INTROUVABLE » ;
3. `plan.totals.suites` 22→21 (fiche périmée) → ✗ divergence SOURCE + ✗ « dit 22 MAIS
source dit 21 » ;
4. `regression_run` privé d'une suite (plan↔run divergent) → ✗ « incohérence SOURCE :
plan.suites=22 ≠ run len=21 » ;
5. restauré = green (rc=0).
Working tree byte-restauré via `git checkout -- <fichier précis>` (**JAMAIS**
`git checkout .` / `git clean`, interdits absolus). **7 gates re-verts** :
`validate_json` / `check_docs` / `guard_constraints` / `check_artifacts` /
`check_ci_integrity` / `check_readme_claims` / `check_regression` (tous rc=0).
- En-tête du gate mis à jour (inventaire des surfaces couvertes : nouvelle entrée
« COMPTE de suites gated de la fiche devops ») + `ci/README.md` (recap
`check-readme-claims` : nouvelle clause du compte de suites gated).
- **Note memory** : la classe « comptes de tests » (méthodes `def test_`) est couverte
sur 4 surfaces + les 5 per-suite ; le COMPTE de **suites** (`plan.totals.suites`) est
une classe voisine — désormais gaté sur ses DEUX surfaces (fiche QA agrégat déjà gaté ·
fiche DevOps cellule, ce bloc).
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ;
édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact).
- **Auto-score 4Big** : 96/100.
## Session `20260801_203424` · Buffer S8 · Audit 5D (4e surface du même README) : gate de l'IDENTITÉ des **LIVRABLES AUDITÉS** — la liste des hand-off `out/*.json` que l'audit de second niveau LIT comme matière première
**Contexte** : la série anti-dérive Sprint-8 gate chaque surface data-derived contre
sa source de vérité (CLAUDE.md #6, roadmap fonctionnellement close). Trois blocs
`audit_5d` de `ci/check_readme_claims.sh` couvrent déjà le MÊME README : la **synthèse**
(17 contrôles / 5 dimensions + ventilation verdict), l'**énumération des open items**
(`control→dimension→propriétaire`) et l'**IDENTITÉ des 5 dimensions** (`id→titre→
reference`). Une **4e surface** du même README restait ungated.
**Surface trouvée (ungated)** : `05_deliverables_mvp/qa/audit_5d/README.md:21-27` —
section « ## Livrables audités (source = leur `out/`) » : une **liste de 5 puces** qui
énumère À LA MAIN les hand-off `out/*.json` que l'auditeur charge réellement
(`crm/workflow_vente/out/workflow.json` · `crm/dossier_vente/out/doctype_oto_dossier_
vente.json` · `crm/commissions/out/commission_plan.json` · `fiscal/ecf_dgii/out/
ecf_plan.json` · `legal/confotur/out/doctype_confotur_application.json`). Aucun bloc ne
la touchait — vérifié : le seul `audited_artifacts` de tout le gate était dans un
**commentaire** ; les 3 blocs audit_5d ne `load()` que `controls_total`/`totals`/
`open_items`/`dimensions[]`.
**Source faisant autorité** : `qa/audit_5d/out/MANIFEST.json` clé `audited_artifacts`
(byte-gaté par `check_artifacts` ⇒ reproduit à chaque build via
`sorted(ARTIFACT_PATHS.values())` dans `audit_5d_gen.py:61` — le registre des hand-off
que `qalib/artifacts.py::load_artifacts()` charge). Le README énumère les 5 chemins
dans un **ORDRE DIFFÉRENT** du MANIFEST (workflow 2e ici, 3e au MANIFEST trié) ⇒
comparaison par **ENSEMBLE**.
**Dérive silencieuse fermée** : brancher un nouveau livrable amont sur l'audit
(nouvelle entrée `ARTIFACT_PATHS` → nouveau chemin dans `audited_artifacts`) ou en
retirer un laisse la section README **périmée en silence** pendant que l'audit lit un
autre jeu de fichiers ⇒ le lecteur/banquier croirait l'audit couvrir un **périmètre
qu'il ne couvre plus** (l'invention #6 interdit), qu'aucune suite `tests/` (FONCTIONS
de contrôle, pas la prose) n'attrape. **État courant** : les 5 puces == registre
exactement (rien à réécrire · anti-invention #6), le défaut est la **surface ungated**.
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « Audit 5D (4e surface) ·
IDENTITÉ des LIVRABLES AUDITÉS » entre le bloc des 5 dimensions et le bloc DevOps
run-book) : on RECOMPUTE l'ensemble depuis `MANIFEST.audited_artifacts` (zéro
duplication du registre) et on parse les puces `` - `<chemin>` `` de la section
« ## Livrables audités » → égalité par ENSEMBLE exigée (absent ET fantôme mordus, via
set-diff `absents`/`en trop`). **Cohérences croisées bonus** : **(a)** chaque chemin
audité RÉSOUT un fichier commité sur disque (`os.path.exists` — le `_load` de l'auditeur
lève `FileNotFoundError` sinon, audit à vide impossible) ; **(b)** |livrables audités|
== |alias distincts d'artefacts référencés par les `controls[].artifacts` de
`audit_spec.json`| (un artefact chargé mais jamais contrôlé, ou un alias contrôlé sans
chemin de chargement → `KeyError` runtime, désynchronise). Section absente = échec
(traçabilité #6). `import os` ajouté au bloc (non importé globalement).
**6 morsures vérifiées** (4 côté README · 2 côté source) :
1. README puce `workflow.json` retirée → ✗ « absents=[…workflow.json] » ;
2. README puce FANTÔME `demo/scenarios/out/run_sheet.json` ajoutée → ✗ « en trop
(fantôme)=[…run_sheet.json] » ;
3. README typo `commission_plan.json`→`commissions_plan.json` → ✗ absents ET fantôme ;
4. README header de section renommé → ✗ « section « ## Livrables audités »
INTROUVABLE/vide » ;
5. `audit_spec.json` : alias `phantom_alias` ajouté à un contrôle → ✗ « désynchro
registre/spec : 5 livrables MAIS 6 alias distincts contrôlés » (cross-anchor b) ;
6. `MANIFEST.audited_artifacts` : un chemin pointé vers un fichier inexistant → ✗
« INTROUVABLES sur disque » (cross-anchor a) + ✗ README périmé.
Restauré = green : 5 puces == registre · 5 == 5 alias · 5 fichiers résolus · exit 0.
Working tree byte-restauré via `git checkout -- <fichier précis>` (**JAMAIS**
`git checkout .` / `git clean`, interdits absolus). **7 gates re-verts** :
`validate_json` / `guard_constraints` / `check_docs` / `check_artifacts` /
`check_regression` / `check_ci_integrity` / `check_readme_claims` (tous rc=0).
- `ci/README.md` mis à jour : clause de la row récap `check-readme-claims` (surface
audit 5D étendue) + paragraphe détaillé « 4ᵉ surface du même README de l'audit 5D ».
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ;
édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact ; l'exécution
réelle de l'audit `bench` reste côté VPS).
- **Auto-score 4Big** : 96/100.
## Session `20260801_213444` · Buffer S8 · Fiche ONAPI/Legal : 2ᵉ SURFACE PROSE des ENTITÉS PORTEUSES
**Tâche** : **Sprint 8 · buffer** (DevOps CI/CD · QA — anti-dérive doc). Roadmap
plancher atteinte ; on continue de fermer les surfaces prose data-derived encore
ungatées. Cible de cette session : `03_agents/onapi_legal/AGENT.md:54`.
**Surface trouvée (ungatée)** : la fiche ONAPI/Legal décrit le champ Select
`entite_porteuse` du DocType CONFOTUR par « **les 7 entités de CLAUDE.md #Entités
(WAF · WA SRL · AC Arias Cuevas · Consortium ECR DR · Helios RD · Ploutos · 9060
QC)** ». C'est un **COMPTE littéral** (« 7 ») **+** l'**énumération complète** — une
**SECONDE surface prose** du même menu que le champ `entite_porteuse` offrira.
Le bloc « Confotur entités » déjà en place ancre le champ à CLAUDE.md §Entités et
gate la ligne du README `legal/confotur` (surface **(e)**), mais ce motif est ancré
sur `05_deliverables_mvp/legal/confotur/README.md` — **il ne vise pas `03_agents/`**.
La fiche restait donc **hors de tout gate** : recompter la §Entités (aujourd'hui 7)
n'atteignait jamais cette phrase.
**Dérive silencieuse fermée** : AJOUTER / RETIRER / RENOMMER une entité dans
CLAUDE.md §Entités (source ancre) → régénérer propage le menu de l'artefact **et** la
ligne du README confotur, MAIS la fiche onapi_legal se **périme en silence** — elle
annoncerait « 7 entités » quand il y en a 8, ou nommerait `Ploutos` là où le menu ne
l'offre plus. L'agent ERPNext Backend qui seede le DocType croirait le déroulant
couvrir un périmètre d'entités qu'il ne couvre plus (l'**invention #6** interdit).
Aucune suite `tests/` (FONCTIONS de build du DocType, pas la prose d'une fiche) ne
l'attrape. **État courant** : la fiche == §Entités == menu artefact exactement
(rien à réécrire · #6) ; le défaut était la surface **ungatée**.
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « Fiche ONAPI/Legal ·
2ᵉ SURFACE PROSE des ENTITÉS PORTEUSES », inséré juste après le bloc « Confotur
entités » et avant le bloc Chat OTOIA) : on **re-dérive** la liste depuis les tokens
en gras de la section « ## Entités » de CLAUDE.md (source ancre · zéro duplication)
et on exige de la fiche — **(a)** le COMPTE littéral (« les N entités ») ==
|§Entités| ; **(b)** l'énumération == §Entités **ORDRE EXACT** (absent ET fantôme
mordus par set-diff + comparaison ordonnée) ; **(c)** l'ancre « CLAUDE.md #Entités »
citée sur la ligne ; **(d)** cross-check bonus — l'énumération == options
`entite_porteuse` de l'artefact CONFOTUR byte-gaté (la fiche décrit **exactement** le
menu que le DocType offrira). Ligne absente = échec (traçabilité #6).
**6 morsures vérifiées** (5 côté fiche · 1 côté artefact) :
1. compte `7`→`8` → ✗ (a) « fiche dit 8 entités MAIS §Entités en porte 7 » ;
2. `Ploutos` retiré de l'énumération → ✗ (b) « absent(s)=['Ploutos'] » ;
3. entité FANTÔME `Fantom SA` ajoutée → ✗ (b) « fantôme(s)=['Fantom SA'] » ;
4. réordre `WAF`↔`WA SRL` → ✗ (b) « ordre : fiche […] ≠ §Entités […] » ;
5. ancre `#Entités` retirée de la ligne → ✗ (c) « ne cite pas CLAUDE.md #Entités » ;
6. option `Ploutos`→`Ploutos SRL` dans l'artefact CONFOTUR → ✗ (d) « fiche […] ≠
menu artefact […] → la fiche décrit un menu que le DocType n'offre pas ».
Restauré = green : (a)(b)(c)(d) tous ✓ · exit 0. Working tree byte-restauré via
`git checkout -- <fichier précis>` (**JAMAIS** `git checkout .` / `git clean`,
interdits absolus). **7 gates re-verts** : `validate_json` / `guard_constraints` /
`check_docs` / `check_artifacts` / `check_regression` / `check_ci_integrity` /
`check_readme_claims` (tous rc=0).
- `ci/README.md` mis à jour : paragraphe « Entités porteuses du DocType CONFOTUR »
étendu d'une clause « **2ᵉ surface prose (fiche ONAPI/Legal)** » (compte + liste +
cross-check menu artefact).
- **Note anti-stale** : la mémoire `agent-fiche-role-attrs-ungated` est désormais
**périmée** — les attributs des fiches mobile / rendu / ifc_speckle (niveau, scope,
modules, permissions, entité) SONT déjà gatés (blocs « Fiche Mobile » et « Fiches
Faisabilité »). La classe résiduelle réelle est celle des LISTES data-derived
transcrites en prose de fiche, dont cette session ferme l'occurrence entités.
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ;
édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact ; l'application
réelle du DocType par `bench` reste côté agent ERPNext Backend).
- **Auto-score 4Big** : 96/100.
---
## Sprint 8 · buffer · Chat OTOIA : gate d'IDENTITÉ de la LISTE D'INVARIANTS CI
**Contexte** : campagne anti-dérive documentaire (Sprint 8 · QA « regression
tests exhaustifs »). Baseline des 7 gates vert avant intervention. Recherche
(sous-agent Explore) de la prochaine classe de surface data-derived transcrite
en prose mais NON gatée → candidat retenu : la section « ## Invariants (14) —
garantis en CI » du README `frontend/chat_otoia` (README:53-60).
**Défaut** : cette section (a) ANNONCE un compte `(14)` puis (b) ÉNUMÈRE chaque
invariant par son numéro entre parenthèses ((1)·(2)·…·(11, 13)·(12)·(14)). C'est
la transcription À LA MAIN du REGISTRE réel des invariants du générateur — les
marqueurs `# Invariant N ·` de `chat_otoia_gen.py` (source d'autorité : chaque
marqueur = un contrôle appliqué par `validate()`). Le générateur se self-reporte
en plus dans son message CLI (« schéma + 14 invariants verts. »), 3ᵉ transcription
du même compte. Les blocs Chat OTOIA préexistants du gate couvrent des COMPTES
d'artefact (blocks/mounts/portails) + l'IDENTITÉ persona/langues/endpoint —
AUCUN ne touche la liste d'invariants : surface aveugle. Aucune suite `tests/` ne
teste la PROSE ; le byte-gate d'artefact ignore le `.py` source ⇒ dérive
silencieuse (ajouter `# Invariant 15` ⇒ README périmé sans échec).
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « Chat OTOIA · IDENTITÉ
de la LISTE D'INVARIANTS CI », inséré juste après le bloc « Chat OTOIA identité »
et avant le bloc « Recette ») : on RECOMPUTE le registre depuis le SEUL
registre-source (`re.findall(r"#\s*Invariant\s+(\d+)", chat_otoia_gen.py)`) et on
exige — **(a)** le registre code est un ensemble CONTIGU `1..max` (lacune/doublon
dans le ledger = défaut réel) ; **(b)** le message CLI « N invariants verts »
== |registre| (self-report du `.py` cohérent avec son propre ledger) ; **(c)** le
compte annoncé « ## Invariants (N) » du README == |registre| ; **(d)**
l'ÉNUMÉRATION en prose (ints extraits des groupes `(...)` de la section, borne =
prochain `## `) == registre EXACT — absent ET fantôme mordus par set-diff. Section
ou compte absent = échec (disparition d'un chiffre = régression de traçabilité #6).
**5 morsures vérifiées** (working tree byte-restauré après chacune via
`git checkout -- <fichier précis>`, JAMAIS `git checkout .` / `git clean`) :
1. compte annoncé `14`→`15` → ✗ (c) « annonce (15) MAIS registre en compte 14 » ;
2. numéro `(7)` retiré de la prose → ✗ (d) « absent(s)=[7] » ;
3. numéro fantôme `20` ajouté à la prose → ✗ (d) « fantôme(s)=[20] » ;
4. self-report CLI `14`→`13` → ✗ (b) « générateur dit 13 MAIS registre 14 » ;
5. ledger code `# Invariant 7`→`77` → ✗ (a) « registre NON contigu […77] » +
✗ (d) « absent(s)=[77] · fantôme(s)=[7] » (double morsure : ledger + prose).
Restauré = green : (a)(b)(c)(d) tous ✓ (registre contigu 1..14 · 14 marqueurs).
- `ci/README.md` : la clause « montage Chat OTOIA » de la row `check-readme-claims`
étendue de l'IDENTITÉ de la liste d'invariants (compte + énumération + self-report
CLI recomputés du registre `# Invariant N`, set-diff + contiguïté).
- **7 gates re-verts** : `validate_json` / `guard_constraints` / `check_docs` /
`check_artifacts` / `check_regression` / `check_ci_integrity` /
`check_readme_claims` (tous rc=0).
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ;
édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact).
- **Auto-score 4Big** : 96/100.
---
## Sprint 8 · buffer · SEO : gate d'IDENTITÉ du COMPTE d'INVARIANTS CI
**Contexte** : campagne anti-dérive documentaire (Sprint 8 · QA « regression
tests exhaustifs »). Baseline des 7 gates vert avant intervention. Recherche
(sous-agent Explore) de la prochaine surface data-derived transcrite en prose
mais NON gatée. L'agent a proposé 5 candidates « N invariants » ; chat_otoia
étant déjà fermée au commit précédent, candidat retenu = le générateur SEO
(`05_deliverables_mvp/seo/seo_gen.py` + `seo/README.md`).
**Défaut** : la source d'autorité du COMPTE D'INVARIANTS de validation SEO n'est
PAS un artefact JSON (invisible à `check_artifacts` qui byte-gate les `out/*.json`)
mais le LEDGER NUMÉROTÉ de la fonction `_validate` — les marqueurs `# N ·`
(`# 1 ·` … `# 15 ·`) qui ouvrent chaque contrôle appliqué, analogue du registre
`# Invariant N` de Chat OTOIA. Ce compte est transcrit À LA MAIN à SEPT endroits
qu'AUCUN gate ne couvrait : le titre `## Garanties (N invariants)` du README + son
bloc de commande (« schéma de sortie + N invariants »), plus CINQ chaînes du `.py`
lui-même (docstring module L19, docstring `_validate` L57, aide `build` L24, message
de succès CLI « N invariants verts. » L285, aide du sous-parseur `validate` L298).
Le bloc SEO préexistant du gate ne couvre que des COMPTES d'ARTEFACT (mots-clés
total + par langue, recomputés de MANIFEST.counts) — classe distincte, surface
aveugle. Ajouter un contrôle `# 16 ·` à `_validate` ⇒ README + self-reports du `.py`
mentent en silence pendant que la CI applique 16 contrôles (« vert trompeur » #6)
— ni les suites `tests/` (FONCTIONS, jamais la prose) ni le byte-gate d'artefact
(aveugle au `.py` source) ne l'attrapent.
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « SEO · IDENTITÉ du
COMPTE d'INVARIANTS CI », inséré juste après le bloc SEO mots-clés et avant le bloc
CRM pipeline vente) : on RECOMPUTE le ledger depuis le SEUL registre-source (les
`# N ·` de `_validate`, extraits du corps de la fonction borné au prochain `def `)
et on exige — **(a)** le ledger est CONTIGU 1..N (comparaison de la LISTE triée à
`range(1,N+1)`, ⇒ mord lacune ET doublon) ; **(b)** TOUTE mention « N invariants »
du `.py` == |ledger| (set des self-reports == {N}) ; **(c)** le compte annoncé
« ## Garanties (N invariants) » du README == |ledger| ; **(d)** TOUTE mention
« N invariants » du README == |ledger|. La prose du README RÉSUME les contrôles
(11 puces `·`-séparées ≠ énumération numérotée 1:1) ⇒ on gate le COMPTE partout où
il apparaît, PAS un set-diff de la liste. Section/compte absent = échec (#6).
**5 morsures vérifiées** (working tree byte-restauré après chacune via
`git checkout -- <fichier précis>`, JAMAIS `git checkout .` / `git clean`) :
1. README titre `15`→`16` → ✗ (c) « annonce (16) MAIS ledger 15 » + ✗ (d) « [15, 16] » ;
2. README commande `15`→`14` → ✗ (d) « mention(s) [14, 15] MAIS ledger 15 » ;
3. `.py` succès CLI `15`→`13` → ✗ (b) « self-report(s) [13, 15] MAIS ledger 15 » ;
4. ledger `# 7 ·`→`# 77 ·` (lacune) → ✗ (a) « NON contigu […6, 77, 8…] » ;
5. ledger `# 8 ·`→`# 7 ·` (doublon) → ✗ (a) « NON contigu […7, 7, 9…] »
(la comparaison sur LISTE — non set — attrape le doublon, angle que
l'approche set de chat_otoia aurait raté).
Restauré = green : (a)(b)(c)(d) tous ✓ (ledger contigu 1..15 · 15 marqueurs).
- `ci/README.md` : la clause SEO de la row `check-readme-claims` étendue du COMPTE
d'INVARIANTS CI (annonce + toutes mentions README + 5 self-reports `.py`
recomputés du ledger `# N ·` de `_validate`, contiguïté 1..N).
- **7 gates re-verts** : `guard_constraints` / `validate_json` / `check_docs` /
`check_artifacts` / `check_regression` / `check_ci_integrity` /
`check_readme_claims` (tous rc=0).
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ;
édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact).
- **Auto-score 4Big** : 96/100.
---
## Sprint 8 · buffer · RBAC SPEC §5 : gate de la LIGNE D'AUTO-CONTRÔLE du « résumé »
**Contexte** : suite de la campagne anti-dérive documentaire (Sprint 8 · QA
« regression tests exhaustifs »). Baseline des 7 gates verte avant intervention.
Recherche (sous-agent Explore) de la prochaine surface data-derived transcrite en
prose mais NON gatée — la plupart des COMPTES d'artefact sont déjà fermés. Le
candidat retenu est une surface interne de `rbac/RBAC_50_ROLES_SPEC.md` : le §5.
**Défaut** : la SPEC RBAC a DÉJÀ trois gates (§3 ventilation par portail · §4
colonne Entité · §7 run-book), mais son §5 « Les N rôles (résumé) » restait
AVEUGLE. Ce §5 restitue le total de rôles sur TROIS surfaces prose : (i) le
compte-titre « ## 5. Les 50 rôles (résumé) », (ii) NEUF comptes parenthétiques de
regroupement (`**Ventes (8)**` …) et (iii) une LIGNE d'auto-contrôle arithmétique
`_(6+8+4+7+5+8+4+2+6 = 50.)_`. AJOUTER un rôle au contrat (|rôles| 50→51) laisse le
titre, la somme et le « = 50 » PÉRIMÉS en silence ; ÉDITER `Ventes (8)→(7)` sans
toucher la ligne casse la cohérence titre↔somme — aucune suite `tests/` (FONCTIONS
RBAC, jamais la prose de la SPEC) ni le byte-gate d'artefact ne l'attrape.
**Subtilité (memory `rbac-spec-portail-count-gate` : « §5 by-family is
editorial »)** : les 9 regroupements sont ÉDITORIAUX — « Direction / Gouvernance »
agrège des rôles de familles direction+finance, ≠ la colonne `famille` du contrat.
On NE mappe donc PAS groupe↔famille (ce serait une dérive fausse). On gate
UNIQUEMENT ce qui est data-derived et vérifiable SANS ce mapping.
**Gate ajouté** (`ci/check_readme_claims.sh`, bloc « 1septies) §5 », inséré après
le bloc §4 et avant le bloc apply_plan) : on RECOMPUTE N = |rôles| du contrat
(`rbac_50_roles.json`, byte-gaté par check_artifacts) et on exige — **(a)** le
compte-titre « ## 5. Les N rôles » == N ; **(b)** les 9 comptes de regroupement ==
EXACTEMENT, dans l'ordre, les termes de la ligne arithmétique (cohérence interne
prose↔somme, comparaison de LISTES ordonnées) ; **(c)** l'arithmétique est VRAIE
(Σ termes == membre droit) ; **(d)** le membre droit == N. Section/titre/ligne/
comptes absents = échec (#6).
**5 morsures vérifiées** (working tree byte-restauré après chacune via
`git checkout -- <fichier précis>`, JAMAIS `git checkout .` / `git clean` ;
gate rc=1 sous mutation, rc=0 restauré) :
1. titre `50`→`51` → ✗ (a) « titre dit 51 rôles MAIS le contrat en compte 50 » ;
2. regroupement `Ventes (8)`→`(9)` → ✗ (b) « [6, 9, 4…] ≠ termes [6, 8, 4…] » ;
3. ligne `= 50.`→`= 49.` → ✗ (c) « écrit = 49 MAIS Σ = 50 » + ✗ (d) « totalise
49 MAIS le contrat compte 50 » (double morsure) ;
4. terme `6+8+…`→`7+8+…` → ✗ (b) « termes [7, 8…] ≠ [6, 8…] » + ✗ (c) « = 50
MAIS Σ = 51 » (double morsure) ;
5. suppression de la ligne d'auto-contrôle → ✗ « ligne … INTROUVABLE » (absence).
Restauré = green : (a)(b)(c)(d) tous ✓ (titre 50 · 9 comptes == 6+8+4+7+5+8+4+2+6
· Σ=50 · membre droit 50 == |rôles|).
- `ci/README.md` : la row `check-readme-claims` étendue de la clause §5 (compte-
titre + 9 comptes de regroupement éditoriaux + ligne d'auto-contrôle, sans
mapping groupe↔famille).
- **7 gates re-verts** : `guard_constraints` / `validate_json` / `check_docs` /
`check_artifacts` / `check_regression` / `check_ci_integrity` /
`check_readme_claims` (tous rc=0).
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ;
édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact).
- **Auto-score 4Big** : 96/100.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,328 @@
# Daily Report · 2026-08-02 · Claude Code DTP Worker
**Session** : `20260802_110539`
**Phase roadmap** : Sprint 8 · *Launch + Buffer* — livrables « Regression tests
exhaustifs » (QA) + « Corrections finales » (buffer).
## Tâche exécutée
**Sprint 8 · QA — sweep de vérification launch-readiness sur arbre propre + reprise
du canal `daily_reports/`.**
Ce rapport comble un **trou de reporting** : le canal canonique des rapports
quotidiens ([`04_roadmap/ROADMAP_8_WEEKS_OR_LESS.md`](../../04_roadmap/ROADMAP_8_WEEKS_OR_LESS.md)
§Escalations · référencé par les 13 fiches `03_agents/*/AGENT.md`) n'avait **plus
d'instantané consolidé depuis `2026-07-30`**, alors que ~2 douzaines de sessions
buffer ont continué. Leur trace fine existe (journal par session
[`05_activity_log/`](../../05_activity_log/2026-08-02.md)) mais **aucun snapshot
sourcé de l'état MVP** n'existait pour la phase Sprint 8. Ce rapport le produit,
chaque chiffre **re-vérifié contre l'artefact commité** ci-dessous (anti-invention
`CLAUDE.md` #6 · zéro donnée nouvelle).
## Contexte / analyse
- Relu `CLAUDE.md`, la roadmap, `MEMORY.md`, le journal `05_activity_log/2026-08-02.md`,
les fiches `crm` + `bim` (audit d'exactitude doc-vs-réalité).
- **Tous les sprints 1→7 sont livrés côté repo** ; on est en **buffer Sprint 8**.
La série récente a épuisé les surfaces driftables évidentes en **durcissant des
gates existants** (dernier : blind-spot « comptes de tests par suite » sur la
cellule `rbac` pointant une SPEC, commit `ca15e7e`).
- **Décision** (workflow #5 · CLAUDE.md — ne pas empiler un gate redondant) : plutôt
qu'un gate N+1, **auditer l'exactitude** des fiches agents + **produire l'instantané
QA manquant**. La phase Sprint 8 attend précisément une **vérification de régression
exhaustive**, pas de nouveau module (contrainte « pas d'invention de modules »).
## Réalisé
- **Sweep des 7 gates CI** sur l'arbre propre — tous **verts (exit 0)** :
`check_ci_integrity` · `check_artifacts` (reproductibilité byte-identique des
`out/`) · `check_docs` · `check_readme_claims` · `check_regression` ·
`guard_constraints` · `validate_json`. Détail : [`ci/README.md`](../../ci/README.md).
- **Audit doc-vs-réalité de 2 fiches agents** (interdit CLAUDE.md « documenter du
code sans vérifier son existence courante ») :
- `03_agents/crm/AGENT.md` — table des livrables **exacte** : `commissions=25`,
`dossier_vente=31`, `workflow_vente=25`, total **81 tests**, recomptés depuis
`plan.suites[*].test_methods` de
[`regression_plan.json`](../qa/regression/out/regression_plan.json). ✅
- `03_agents/bim/AGENT.md` — son **seul point de contact commité** cité,
`faisabilite/generator/genlib/renderer.py`, **existe** et rend bien le **Volet 4 ·
Ingénierie** en placeholders `{{…}}` (`_v`/`_money`/`_num` · anti-invention #6).
Aucune sur-promesse d'artefact BIM en-repo. ✅
- **Reprise du canal `daily_reports/`** (ce fichier) : instantané MVP consolidé sourcé.
## État courant MVP · 100 % sourcé (aucun chiffre saisi à la main)
| Dimension | Valeur | Verdict | Source commitée |
|---|---|---|---|
| Qualité 4Big | **22/22 modules à 100/100** (seuil 95 · #5) | `PASS` | [`qa/audit_4big/out/quality_report.json`](../qa/audit_4big/out/quality_report.json) |
| Régression (plan) | **22 suites · 564 méthodes** · 0 sous le seuil (min 10) | `PASS` | [`qa/regression/out/regression_plan.json`](../qa/regression/out/regression_plan.json) |
| Régression (run agrégé) | **22 suites · 564 tests · 564 passés · 0 échec · 0 erreur** | `PASS` | [`qa/regression/out/regression_run.json`](../qa/regression/out/regression_run.json) |
| Recette roadmap | **15 promesses** (8 livrables sprint + 7 métriques MVP) · **15 `in_repo`** | `true` | [`qa/acceptance/out/acceptance_matrix.json`](../qa/acceptance/out/acceptance_matrix.json) |
| Fiches agents | **13 agents** | — | `03_agents/*/AGENT.md` |
| Gates CI | **7 gates statiques** verts + reproductibilité `out/` byte-identique | vert | [`ci/README.md`](../../ci/README.md) |
## Vérifications effectuées (en-repo, sans toucher au VPS)
- 7 gates CI → **exit 0** sur l'arbre propre (dont `check_artifacts` : tout `out/`
se régénère à l'identique depuis son générateur `build`).
- Recalcul indépendant des agrégats (`python3` sur les artefacts) **concordant**
avec les valeurs affichées : régression 564/564, qualité 22/22, recette 15/15.
- Liens Markdown de ce rapport résolus vers des cibles existantes (pré-vérifiés).
## Non fait (hors périmètre worker · touche au VPS)
- Déploiement production réel (bench migrate, import fixtures, câblage nginx/systemd,
builds stores, indexation SEO, voix Amélie) → **VPS `153.75.250.214`** · agents
DevOps/ERPNext, plan ordonné dans `devops/deploy_runbook` (`CLAUDE.md` #8).
- Enregistrement `act_runner` Gitea + exécution serveur de la baseline Playwright.
## Note de convention (réconciliation doc-vs-réalité)
Deux canaux de journalisation coexistent et sont désormais explicités :
1. **`05_activity_log/YYYY-MM-DD.md`** — journal **technique par session** (riche,
tenu à chaque session · instruction mandat #6). **Canal courant de facto.**
2. **`05_deliverables_mvp/daily_reports/YYYY-MM-DD.md`** — **instantané consolidé
stakeholder** (roadmap §Escalations). Repris ici après lapsus depuis `2026-07-30`.
Les deux servent des lecteurs différents (technique vs. direction) et ne sont **pas
des doublons**. Un lecteur d'`AGENT.md` cherchant « le journal récent » doit se
référer au **canal 1** pour le détail par session, au **canal 2** pour la synthèse.
## Prochaine tâche suggérée
- Maintenir le canal `daily_reports/` à jour à chaque session buffer (1 instantané/jour).
- Poursuivre l'audit d'exactitude des 11 fiches `AGENT.md` restantes (mêmes contrôles
que `crm`/`bim` ci-dessus) — repérer toute sur-promesse d'artefact ou compte périmé.
- Buffer : ne durcir un gate que si un **blind-spot est prouvé par mutation-test**
(mémoire `verify-uncovered-before-gating`), jamais un gate redondant (#5).
---
**Auto-score 4Big de la sweep de vérification : 96/100.** Réserve 4 : audit
d'exactitude limité à 2 des 13 fiches cette session (les 11 autres restent à
re-vérifier) ; déploiement serveur hors périmètre (VPS). État launch-readiness
confirmé et **intégralement sourcé** : 22/22 qualité · 564/564 régression · 15/15
recette · 7 gates verts.
---
## Clôture de journée · addendum session `20260802_120544`
> Cet addendum est **additif** : le corps ci-dessus reste le compte-rendu horodaté
> de la session `110539` (record historique intact). Il **acte la clôture** de la
> campagne d'audit que ce même rapport listait en « Prochaine tâche suggérée #2 »,
> réalisée plus tard le même jour, et **lève la réserve 4** devenue caduque.
**Ce qui a fermé depuis 11:11** — l'**audit d'exactitude doc-vs-réalité des 11
fiches `AGENT.md` restantes** (devops · erpnext_backend · faisabilite ·
frontend_console · ifc_speckle · mobile · onapi_legal · publiciste · qa · rendu ·
seo) a été mené à terme (commit `2bc3afb`, session `113542`) : **0 dérive réelle**.
Les 2 « dérives » remontées par les agents ont été **levées comme faux positifs de
convention**, prouvés avant toute action (mémoire `verify-uncovered-before-gating`) :
- **onapi_legal** — chemins backtick `../rbac/…` / `../crm/workflow_vente/…` **relatifs
à l'arbre des livrables** (`05_deliverables_mvp/legal/`), **miroir du code** du
générateur, pas fiche-relatifs ; code-spans volontairement non link-checkés
(mémoire `backtick-path-escapes-check-docs`).
- **faisabilite** — chemins `/opt/oto/…`, version `{X}`, section « Composants à
créer » : **VPS-runtime assumés**, hors périmètre worker (#8), déjà réconciliés
§15-17 / §27-29 ; seule retouche = **note de lisibilité** (bandeau VPS-runtime vs
template committé), zéro chiffre.
**Bilan de la campagne (levée de la réserve 4)** — les **13/13 fiches**
`03_agents/*/AGENT.md` sont désormais **auditées exactes** (crm/bim en session
`110539` + les 11 ici). Classe close : mémoire `fiche-accuracy-audit-closed`.
**État courant re-vérifié à la clôture** (recompute indépendant `python3` sur les
artefacts commités · aucun chiffre saisi à la main) — **inchangé et vert** :
| Dimension | Valeur | Verdict | Source |
|---|---|---|---|
| Qualité 4Big | **22/22 modules** (seuil `pass_score` 95) | `PASS` | `qa/audit_4big/out/quality_report.json` |
| Régression (run) | **22 suites · 564 ran · 564 passés · 0 échec · 0 erreur** | `PASS` | `qa/regression/out/regression_run.json` |
| Recette roadmap | **15 promesses** `in_repo` | `true` | `qa/acceptance/out/acceptance_matrix.json` |
| Fiches agents | **13/13 auditées exactes** | — | `03_agents/*/AGENT.md` |
| Gates CI | **7 gates** re-joués → **exit 0** + reproductibilité `out/` byte-identique | vert | `ci/README.md` |
**Prochaine tâche (révisée)** — la file d'audit fiches est **vide** (13/13 clos). Le
buffer Sprint 8 reste en posture de **veille** : maintenir `daily_reports/` (1
instantané/jour), et ne durcir un gate **que si** un blind-spot est **prouvé par
mutation-test** (`verify-uncovered-before-gating`), jamais un gate redondant (#5).
**Auto-score 4Big de la clôture : 97/100.** Réserve 3 : purement une passe de
**vérification + réconciliation de reporting** (aucun nouveau livrable — attendu en
phase buffer saturée) ; déploiement serveur hors périmètre (VPS · #8).
---
## Fin de journée · addendum session `20260802_143555`
> **Additif** (comme l'addendum précédent) : le corps et l'addendum de clôture
> restent intacts. Cet ajout **consolide les 4 sessions buffer intercalées entre la
> clôture `120544` et 14:35** (commits `cb8d931` · `8dcedb0` · `576e78b` · `bd3572b`),
> absentes de l'instantané stakeholder alors que leur trace fine existe déjà au canal 1
> ([`05_activity_log/2026-08-02.md`](../../05_activity_log/2026-08-02.md)). Tenir le
> canal 2 à jour à chaque session buffer était la **« Prochaine tâche » #1** de ce
> rapport (l.84). Chaque chiffre ci-dessous est **re-vérifié contre l'artefact commité**
> (recompute `python3` indépendant · anti-invention `CLAUDE.md` #6 · zéro donnée nouvelle).
**Les 4 sessions consolidées** — toutes en posture buffer (aucun nouveau module ·
contrainte « pas d'invention de modules ») :
- **`123546` (`cb8d931`)** — *dérive doc-vs-réalité corrigée* : les 2 docs de mandat de
tête (`AGENTS_EXISTING_ASSETS` titre + `MASTER_PROMPT §28`) disaient « **12 agents** »
alors que le **13ᵉ** (publiciste) est livré+gaté → correction **12→13** (source unique
`03_agents/`) + clarification du statut publiciste (**LIVRÉ** mandat · runtime VPS hors
#8) + extension de `check_readme_claims` (réutilise `n_agents`) pour ancrer ces 2 docs.
- **`130548` (`8dcedb0`)** — *ancrage anti-dérive* : les 3 surfaces vives de
`GAP_ANALYSIS_SPRINT1` citant le compte d'agents (dont la coche de couverture DoD) sont
ré-ancrées sur `n_agents` (re-dérivé, jamais une liste à la main).
- **`133549` (`576e78b`)** — *enquête anti-dérive, ZÉRO gate ajouté (#5)* : le `roles_couverts`
(« 44 ») des 3 manifestes s'est révélé **transitivement mono-sourcé** (même fonction
`roles_by_portail` sur l'unique contrat `rbac_50_roles.json`) → un cross-gate serait
**redondant** ; garde-fou documenté dans [`ci/README.md`](../../ci/README.md) pour
prévenir un gate N+1 inutile.
- **`140554` (`bd3572b`)** — *audit d'EFFICACITÉ des gates, ZÉRO gate ajouté (#5)* :
**mutation-testing** du suite (7 mutations ciblées sur 4 gates à oracle recomputé) →
**7/7 ROUGE** prouvés (« vert » ≠ « faux-vert »). Confirme que le suite **mord**
réellement en phase de bascule launch.
**État courant MVP · re-vérifié à 14:35 · 100 % sourcé** (recompute `python3` indépendant
sur les artefacts commités · aucun chiffre saisi à la main) — **inchangé et vert** :
| Dimension | Valeur | Verdict | Source commitée |
|---|---|---|---|
| Qualité 4Big | **22/22 modules à 100/100** (seuil `pass_score` **95** · #5) | `PASS` | [`qa/audit_4big/out/quality_report.json`](../qa/audit_4big/out/quality_report.json) |
| Régression (run) | **22 suites · 564 ran · 564 passés · 0 échec · 0 erreur** | `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) |
| Fiches agents | **13/13 auditées exactes** (classe close) | — | `03_agents/*/AGENT.md` |
| Gates CI | **7 gates statiques** re-joués → **exit 0** + reproductibilité `out/` byte-identique | vert | [`ci/README.md`](../../ci/README.md) |
| Câblage CI | `gate.needs` = **30 jobs** agrégés · **1** seul job manuel (`e2e-baseline`) | intègre | [`.gitea/workflows/ci.yml`](../../.gitea/workflows/ci.yml) |
**Vérifications effectuées à la clôture** (en-repo, sans toucher au VPS) :
- **7 gates CI → exit 0** sur l'arbre propre (`check_artifacts` · `check_ci_integrity` ·
`check_docs` · `check_readme_claims` · `check_regression` · `guard_constraints` ·
`validate_json`).
- Recompute indépendant des agrégats concordant : **22/22** qualité (scores distincts =
{100}) · **564/564** régression · **15/15** recette · **13** fiches · `gate.needs` = **30**.
- Arbre `git status --porcelain` **vide** avant/après ; aucune commande touchant au VPS.
**Prochaine tâche (révisée)** — file d'audit fiches **vide** (13/13 clos), campagne de
gates **saturée** (dernier audit d'efficacité 7/7 ROUGE). Le buffer Sprint 8 reste en
**veille** : maintenir `daily_reports/` (1 instantané/jour · fait ici), et **ne durcir un
gate que si un blind-spot est prouvé par mutation-test** (`verify-uncovered-before-gating`),
jamais un gate redondant (#5). Déploiement production réel reste **hors périmètre worker**
(VPS `153.75.250.214` · agents DevOps/ERPNext · #8).
**Auto-score 4Big de la consolidation : 97/100.** Réserve 3 : passe de
**réconciliation de reporting** (canal 2 rattrapé sur 4 sessions · aucun nouveau
livrable — attendu en buffer saturé) ; déploiement serveur hors périmètre (VPS · #8).
État launch-readiness **inchangé et intégralement sourcé**.
## Fin de journée · addendum session `20260802_160601`
> **Additif** (3ᵉ, même convention que les deux précédents) : le corps et les deux
> addenda restent intacts. Cet ajout consolide les **2 sessions buffer intercalées
> entre l'addendum `143555` (14:35) et 15:36** (commits `49123ec` · `dcb463e`),
> absentes de l'instantané stakeholder alors que leur trace fine existe au canal 1
> ([`05_activity_log/2026-08-02.md`](../../05_activity_log/2026-08-02.md)). Tenir le
> canal 2 à jour à chaque session buffer était la tâche de veille #1 de l'addendum
> précédent (l.201). Chaque chiffre ci-dessous est **re-vérifié contre l'artefact
> commité** (recompute `python3` indépendant · anti-invention `CLAUDE.md` #6 · zéro
> donnée nouvelle).
**Les 2 sessions consolidées** — un seul livrable net, un **outil DevOps** (pas un
nouveau module · contrainte « pas d'invention de modules » respectée) :
- **`150601` (`49123ec`)** — *DevOps outillage : miroir LOCAL du gate CI* : ajout de
[`run_ci.sh`](../../run_ci.sh) à la racine, qui **dérive** la liste des jobs de
`gate.needs` dans [`.gitea/workflows/ci.yml`](../../.gitea/workflows/ci.yml) (zéro
liste en dur · anti-dérive par construction · suit `ci.yml`) et rejoue les **30 jobs**
(7 gates `ci/*.sh` + 23 suites module) en une commande. **PAS un gate (#5)** : la
racine ∉ `ci/*.sh`, donc hors INV-B ; parseur `awk` anti-commentaire ; chemin d'échec
prouvé (`EXIT=1` + dump). Doc `README` + `ci/README`. Consigné en mémoire
(*local-ci-runner*).
- **`153601` (`dcb463e`)** — *durcissement du même outil* : le décompte **30/23/7** du
commentaire d'en-tête, jusque-là **figé** (risque de dérive silencieuse si `ci.yml`
gagne/perd un job), est retiré et **DÉRIVÉ à l'exécution** — la valeur affichée par
`run_ci.sh` provient désormais du parse de `gate.needs`, jamais d'un nombre saisi.
**État courant MVP · re-vérifié à 16:06 · 100 % sourcé** (recompute `python3` indépendant
sur les artefacts commités · aucun chiffre saisi à la main) — **inchangé et vert** :
| Dimension | Valeur | Verdict | Source commitée |
|---|---|---|---|
| Qualité 4Big | **22/22 modules à 100/100** (scores distincts = {100} · seuil 95 · #5) | `PASS` | [`qa/audit_4big/out/quality_report.json`](../qa/audit_4big/out/quality_report.json) |
| Régression (run) | **22 suites · 564 ran · 564 passés · 0 échec · 0 erreur · 0 skip** | `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) |
| Fiches agents | **13/13 auditées exactes** (classe close) | — | `03_agents/*/AGENT.md` |
| Câblage CI | `gate.needs` = **30 jobs** (7 gates `ci/*.sh` + 23 suites module) | intègre | [`.gitea/workflows/ci.yml`](../../.gitea/workflows/ci.yml) |
| Miroir local | `run_ci.sh` rejoue le gate → **30 PASS · 0 FAIL · 0 SKIP** | vert | [`run_ci.sh`](../../run_ci.sh) |
**Vérifications effectuées à la clôture** (en-repo, sans toucher au VPS) :
- **`run_ci.sh` → 30 PASS · 0 FAIL · 0 SKIP** (miroir complet du gate, décompte 30/7/23
dérivé à l'exécution de `gate.needs`).
- Recompute indépendant des agrégats concordant : **22/22** qualité (scores = {100}) ·
**564/564** régression · **15/15** recette (verdict `true`) · **13** fiches ·
`gate.needs` = **30**.
- Arbre `git status --porcelain` **vide** avant l'édition ; aucune commande touchant au VPS.
**Prochaine tâche (inchangée)** — buffer Sprint 8 en **veille saturée** : file d'audit
fiches **vide** (13/13 clos), campagne de gates **saturée**. Maintenir `daily_reports/`
à chaque session (fait ici), et **ne durcir un gate que si un blind-spot est prouvé par
mutation-test** (*verify-uncovered-before-gating*), jamais un gate redondant (#5).
Déploiement production réel reste **hors périmètre worker** (VPS `153.75.250.214` · #8).
**Auto-score 4Big de la consolidation : 97/100.** Réserve 3 : passe de
**réconciliation de reporting** (canal 2 rattrapé sur 2 sessions · le seul livrable net
est l'outil DevOps `run_ci.sh`, déjà gaté par construction — attendu en buffer saturé) ;
déploiement serveur hors périmètre (VPS · #8). État launch-readiness **inchangé et
intégralement sourcé**.
## Fin de journée · addendum session `20260802_180604`
> **Additif** (4ᵉ, même convention que les trois précédents) : le corps et les trois
> addenda restent intacts. Cet ajout consolide les **3 sessions buffer intercalées entre
> l'addendum `160601` (16:06) et 18:06** (commits `59eb4dc` · `68f8c1a` · `d2ab269`),
> absentes de l'instantané stakeholder alors que leur trace fine existe au canal 1
> ([`05_activity_log/2026-08-02.md`](../../05_activity_log/2026-08-02.md)). Tenir le
> canal 2 à jour à chaque session buffer était la tâche de veille #1 de l'addendum
> précédent (l.259). Chaque chiffre ci-dessous est **re-vérifié contre l'artefact
> commité** (recompute `python3` indépendant · anti-invention `CLAUDE.md` #6 · zéro
> donnée nouvelle).
**Les 3 sessions consolidées****aucun nouveau module, aucun gate ajouté (#5)** : trois
retouches de **doc / outillage DevOps** dans la lignée directe de `run_ci.sh`, chacune
une correction d'honnêteté (retrait de comptes figés) plutôt qu'un livrable net :
- **`163601` (`59eb4dc`)** — *fiche `03_agents/devops/AGENT.md`* : documentation du
livrable `run_ci.sh` (indexé au `README` racine mais absent de la fiche de son agent
propriétaire) → 3ᵉ ligne de table + sous-section « outillage local, **PAS un gate #5** »
(racine ∉ `ci/`, hors INV-B). Zéro module, zéro gate ; `check_docs` valide le lien
`../../run_ci.sh`.
- **`170601` (`68f8c1a`)** — *`run_ci.sh` : `-h|--help` + en-tête `--static` honnête*
(UX du lanceur · **PAS un gate #5**) — le lanceur documente désormais son propre usage,
toujours hors périmètre des gates.
- **`173603` (`d2ab269`)** — *`ci/README.md` : retrait des comptes CI figés « 7 gates /
23 suites »* (même fix d'honnêteté que `dcb463e`, un cran plus haut · anti-invention #6,
**PAS un gate #5**) — le décompte exact reste **dérivé** par `run_ci.sh` de `gate.needs`,
jamais re-transcrit à la main dans la prose.
**État courant MVP · re-vérifié à 18:06 · 100 % sourcé** (recompute `python3` indépendant
sur les artefacts commités · aucun chiffre saisi à la main) — **inchangé et vert** :
| Dimension | Valeur | Verdict | Source commitée |
|---|---|---|---|
| Qualité 4Big | **22/22 modules à 100/100** (scores distincts = {100} · seuil 95 · #5) | `PASS` | [`qa/audit_4big/out/quality_report.json`](../qa/audit_4big/out/quality_report.json) |
| Régression (run) | **22 suites · 564 ran · 564 passés · 0 échec · 0 erreur · 0 skip** | `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) |
| Fiches agents | **13/13 auditées exactes** (classe close) | — | `03_agents/*/AGENT.md` |
| Câblage CI | `gate.needs` = **30 jobs** (7 gates `ci/*.sh` + 23 suites module) | intègre | [`.gitea/workflows/ci.yml`](../../.gitea/workflows/ci.yml) |
| Miroir local | `run_ci.sh` rejoue le gate → **30 PASS · 0 FAIL · 0 SKIP** | vert | [`run_ci.sh`](../../run_ci.sh) |
**Vérifications effectuées à la clôture** (en-repo, sans toucher au VPS) :
- **`run_ci.sh` → 30 PASS · 0 FAIL · 0 SKIP** (miroir complet du gate, décompte 30/7/23
dérivé à l'exécution de `gate.needs`).
- Recompute indépendant des agrégats concordant : **22/22** qualité (scores = {100}) ·
**564/564** régression · **15/15** recette (verdict `true`) · **13** fiches ·
`gate.needs` = **30**.
- Arbre `git status --porcelain` **vide** avant l'édition ; aucune commande touchant au VPS.
**Prochaine tâche (inchangée)** — buffer Sprint 8 en **veille saturée** : file d'audit
fiches **vide** (13/13 clos), campagne de gates **saturée**, audit doc-vs-réalité relancé
cette session → **aucune dérive** trouvée. Maintenir `daily_reports/` à chaque session
(fait ici), et **ne durcir un gate que si un blind-spot est prouvé par mutation-test**
(*verify-uncovered-before-gating*), jamais un gate redondant (#5). Déploiement production
réel reste **hors périmètre worker** (VPS `153.75.250.214` · #8).
**Auto-score 4Big de la consolidation : 97/100.** Réserve 3 : passe de
**réconciliation de reporting** (canal 2 rattrapé sur 3 sessions de retouches doc/outillage,
aucun livrable net nouveau — attendu en buffer saturé) ; déploiement serveur hors périmètre
(VPS · #8). État launch-readiness **inchangé et intégralement sourcé**.
@@ -114,7 +114,9 @@ def _check_derived_arithmetic(brief: dict, manifest: dict) -> list[str]:
"""Reproduit indépendamment les 4 agrégats et compare au manifeste. """Reproduit indépendamment les 4 agrégats et compare au manifeste.
Prouve qu'aucune valeur calculée n'a été « posée » : elle DOIT découler des Prouve qu'aucune valeur calculée n'a été « posée » : elle DOIT découler des
typologies sourcées et du taux canonique 52 %. typologies sourcées et du taux canonique de point d'équilibre. Ce taux est lu
à la SOURCE UNIQUE (`CANONICAL` · CLAUDE.md #9, gaté), jamais recopié en dur
ici — sinon un changement du mandat (#9) casserait cet oracle en silence.
""" """
motifs: list[str] = [] motifs: list[str] = []
rows = finance.typologies(brief) rows = finance.typologies(brief)
@@ -128,8 +130,9 @@ def _check_derived_arithmetic(brief: dict, manifest: dict) -> list[str]:
if all_q and all(r["prix_usd"] is not None for r in rows) else None) if all_q and all(r["prix_usd"] is not None for r in rows) else None)
exp_dop = (sum(q * r["prix_dop"] for q, r in zip(qtes, rows)) exp_dop = (sum(q * r["prix_dop"] for q, r in zip(qtes, rows))
if all_q and all(r["prix_dop"] is not None for r in rows) else None) if all_q and all(r["prix_dop"] is not None for r in rows) else None)
exp_pe = (float(math.ceil(0.52 * exp_total)) pe_pct = finance._pct(deps.CANONICAL["point_equilibre_pct"])
if exp_total is not None else None) exp_pe = (float(math.ceil(pe_pct * exp_total))
if exp_total is not None and pe_pct is not None else None)
for cle, exp in (("total_unites", exp_total), for cle, exp in (("total_unites", exp_total),
("valeur_catalogue_usd", exp_usd), ("valeur_catalogue_usd", exp_usd),
@@ -7,7 +7,7 @@
"required": ["projet"], "required": ["projet"],
"additionalProperties": true, "additionalProperties": true,
"properties": { "properties": {
"projet": { "type": "string", "pattern": "^P0[1-9]$" }, "projet": { "type": "string", "pattern": "^P0[1-9]$", "description": "Code projet canonique (P01..P09) · domaine ancré sur CLAUDE.md §Projets." },
"nom": { "type": "string" }, "nom": { "type": "string" },
"synthetique": { "type": "boolean", "description": "true = fixture de test, jamais publiable." }, "synthetique": { "type": "boolean", "description": "true = fixture de test, jamais publiable." },
"sources": { "sources": {
@@ -18,7 +18,7 @@
"projet": { "projet": {
"type": "string", "type": "string",
"pattern": "^P0[1-9]$", "pattern": "^P0[1-9]$",
"description": "Code projet canonique (P01..P09)." "description": "Code projet canonique (P01..P09) · domaine ancré sur CLAUDE.md §Projets."
}, },
"template_version": { "template_version": {
"type": "string", "type": "string",
@@ -0,0 +1,94 @@
{
"generated_from": "rbac_50_roles.json (permissions_cibles + portail par rôle)",
"spec_version": "1.0.0",
"source_version": "1.0.0",
"roadmap_ref": "04_roadmap/ROADMAP_8_WEEKS_OR_LESS.md · Sprint 4 l.49 « Frontend Console : 5 portails (Ventes/Construction/Achat/Compta/Direction) »",
"counts": {
"workspaces": 5,
"roles_couverts": 44,
"liens_total": 53,
"raccourcis_total": 20
},
"brand": {
"note": "Tokens de marque luxury dark+doré (CLAUDE.md #4). Appliqués par la couche thème desk (Website Theme / CSS custom) déposée par l'agent Frontend sur le VPS — HORS de ce worker (#8). Repris ici pour le hand-off, jamais fabriqués.",
"fond": {
"valeur": "#0a0a12",
"source": "CLAUDE.md #4"
},
"accent": {
"valeur": "#f0b429",
"source": "CLAUDE.md #4"
},
"polices": [
{
"valeur": "Fraunces",
"source": "CLAUDE.md #4"
},
{
"valeur": "Cormorant Garamond",
"source": "CLAUDE.md #4"
}
]
},
"module_note": "Workspace.module laisse a None (a_confirmer) : l'agent ERPNext Backend le fixe au module de l'app OTO a l'import (#8). Le worker ne fabrique pas de nom d'app.",
"doctypes_custom_a_creer": [
"CONFOTUR Application",
"Faisabilité",
"Publiciste Log"
],
"workspaces": [
{
"portail": "ventes",
"workspace": "OTO Ventes",
"nb_cartes": 4,
"nb_raccourcis": 4,
"nb_liens": 11,
"nb_roles": 12,
"doctypes_custom": [
"CONFOTUR Application",
"Publiciste Log"
]
},
{
"portail": "construction",
"workspace": "OTO Construction",
"nb_cartes": 4,
"nb_raccourcis": 4,
"nb_liens": 9,
"nb_roles": 10,
"doctypes_custom": [
"Faisabilité"
]
},
{
"portail": "achat",
"workspace": "OTO Achat",
"nb_cartes": 3,
"nb_raccourcis": 4,
"nb_liens": 8,
"nb_roles": 5,
"doctypes_custom": []
},
{
"portail": "compta",
"workspace": "OTO Compta",
"nb_cartes": 4,
"nb_raccourcis": 4,
"nb_liens": 11,
"nb_roles": 8,
"doctypes_custom": []
},
{
"portail": "direction",
"workspace": "OTO Direction",
"nb_cartes": 4,
"nb_raccourcis": 4,
"nb_liens": 14,
"nb_roles": 9,
"doctypes_custom": [
"CONFOTUR Application",
"Faisabilité"
]
}
]
}
File diff suppressed because it is too large Load Diff
+9 -2
View File
@@ -1,4 +1,4 @@
# Actifs existants OTOV7 · Mapping vers les 12 agents DTP # Actifs existants OTOV7 · Mapping vers les 13 agents DTP
## Principe : NE PAS REPARTIR DE ZÉRO ## Principe : NE PAS REPARTIR DE ZÉRO
Chaque agent hérite/réutilise les modules déjà présents avant de créer du nouveau code. Chaque agent hérite/réutilise les modules déjà présents avant de créer du nouveau code.
@@ -112,9 +112,16 @@ Chaque agent hérite/réutilise les modules déjà présents avant de créer du
4. **Capabilities OTOIA** = fondation → chaque agent DTP branche dessus 4. **Capabilities OTOIA** = fondation → chaque agent DTP branche dessus
5. **Éliminer le vieux** : si un module est obsolète, l'archiver dans `_ARCHIVES/`, ne pas laisser deux versions 5. **Éliminer le vieux** : si un module est obsolète, l'archiver dans `_ARCHIVES/`, ne pas laisser deux versions
## 13. Publiciste Agent — NOUVEAU (à créer) ## 13. Publiciste Agent — NOUVEAU
**Rôle** : Maintient automatiquement le site public `vente.otov7.com` à partir des faisabilités. **Rôle** : Maintient automatiquement le site public `vente.otov7.com` à partir des faisabilités.
> **Statut mandat** : la fiche + le générateur déterministe côté dépôt sont **LIVRÉS**
> ([`03_agents/publiciste/AGENT.md`](03_agents/publiciste/AGENT.md) ·
> [`05_deliverables_mvp/publiciste/`](05_deliverables_mvp/publiciste/README.md), gated
> 100/100 · suite de régression `publiciste`). La liste **« À développer »** ci-dessous
> recense le **runtime VPS** (orchestrateur `publiciste.py`, triggers systemd/inotify,
> notifications WhatsApp) qui reste hors périmètre worker (`CLAUDE.md` #8).
**Ressources existantes à réutiliser** : **Ressources existantes à réutiliser** :
- `/opt/oto/config/projets_config.json` (déjà en place) - `/opt/oto/config/projets_config.json` (déjà en place)
- `/opt/oto/config/projets_editor.py` (API GET/POST déjà en place) - `/opt/oto/config/projets_editor.py` (API GET/POST déjà en place)
+6 -3
View File
@@ -23,6 +23,7 @@ NON-NÉGOCIABLES : [`CLAUDE.md`](CLAUDE.md).
| [`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_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) |
| [`tests/`](tests) | Baseline E2E Playwright | | [`tests/`](tests) | Baseline E2E Playwright |
## Les 13 agents ## Les 13 agents
@@ -40,9 +41,11 @@ Transverses : [`devops`](03_agents/devops/AGENT.md) · [`qa`](03_agents/qa/AGENT
- **Qualité 4Big** : **22/22 modules gated à 100/100** (seuil 95 · `CLAUDE.md` #5), verdict `PASS` - **Qualité 4Big** : **22/22 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** : **22 suites gated** couvertes bijectivement vs CI ; le compte de tests - **Régression** : **22 suites gated** couvertes bijectivement vs CI. Le **compte de tests
agrégé faisant autorité est produit par `qa/regression run` (non commité par design) agrégé faisant autorité** vit dans [`qa/regression/out/regression_run.json`](05_deliverables_mvp/qa/regression/out/regression_run.json),
— plan [`qa/regression/out/regression_plan.json`](05_deliverables_mvp/qa/regression/out/regression_plan.json). artefact **commité et byte-gaté** : `ci/check_regression.sh` régénère un `run` frais et exige
l'identité **byte-for-byte** avec la baseline commitée (verdict `PASS`). Recensement déterministe
des suites : [`qa/regression/out/regression_plan.json`](05_deliverables_mvp/qa/regression/out/regression_plan.json).
- **Recette roadmap** : **15 promesses** (8 livrables sprint + 7 métriques MVP), **15 `in_repo` - **Recette roadmap** : **15 promesses** (8 livrables sprint + 7 métriques MVP), **15 `in_repo`
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).
+176 -4
View File
File diff suppressed because one or more lines are too long
+2756 -4
View File
File diff suppressed because it is too large Load Diff
Executable
+167
View File
@@ -0,0 +1,167 @@
#!/usr/bin/env bash
# ============================================================================
# run_ci.sh · OTO Enterprise OS DTP · miroir LOCAL du gate CI
# ----------------------------------------------------------------------------
# Reproduit en une commande, EN LOCAL, exactement l'ensemble de checks que le
# gate de merge Gitea Actions agrège — sans runner, sans réseau (Sprint 8 ·
# DevOps « monitoring / regression exhaustifs »).
#
# POURQUOI ce script existe : chaque session rejouait à la main une boucle
# ad-hoc « for g in guard_constraints validate_json … ; do bash ci/$g.sh ». Une
# telle liste écrite à la main DÉRIVE en silence de ce que la CI enforce
# réellement (un gate ajouté/retiré dans `.gitea/workflows/ci.yml` n'y était
# jamais répercuté) — la même classe de « vert trompeur » que les gates du
# dépôt traquent. Ce script n'a AUCUNE liste en dur : il DÉRIVE la liste des
# checks depuis `gate.needs` de ci.yml (la source de vérité que
# `ci/check_ci_integrity.sh` verrouille déjà) et exécute, pour chaque job, la
# commande `run:` que ci.yml déclare, dans son `working-directory:`. Il ne peut
# donc ni oublier ni inventer un check : ajouter un gate à la CI le fait
# apparaître ici automatiquement, zéro édition.
#
# PÉRIMÈTRE : uniquement les jobs de `gate.needs` — ce qui exclut, par
# construction et à dessein, les jobs manuels `workflow_dispatch` hors gate
# (ex. `e2e-baseline`/`npm test`, qui exige Node) et le job `gate` lui-même.
# Les jobs restants sont tous soit `bash ci/*.sh` (gates statiques), soit
# `python3 -m unittest` (suites de module) → stdlib pur, aucune install pip,
# aucune commande touchant le VPS. Le décompte exact (jobs / gates / suites)
# est DÉRIVÉ de gate.needs et imprimé à l'exécution — jamais figé dans ce
# commentaire, où il se périmerait en silence (anti-invention CLAUDE.md #6).
#
# CE N'EST PAS un gate (aucun nouveau `ci/*.sh`, aucune nouvelle assertion) :
# c'est un LANCEUR de commodité. Son exactitude est STRUCTURELLE (il exécute ce
# que ci.yml déclare), pas une re-transcription à protéger — inutile donc d'y
# adjoindre un gate N+1 (CLAUDE.md #5).
#
# Usage :
# bash run_ci.sh # exécute tout gate.needs (miroir CI complet)
# bash run_ci.sh --static # seulement les gates statiques ci/*.sh (rapide)
# bash run_ci.sh --list # liste les jobs qui SERAIENT lancés, sans exécuter
# bash run_ci.sh --help # affiche cette aide et sort
#
# Sortie : une ligne PASS/FAIL par job + un récapitulatif. Code de sortie 0 ssi
# tous les jobs passent (identique à la sémantique du gate de merge).
# ============================================================================
set -uo pipefail
usage() {
cat <<'EOF'
run_ci.sh · miroir LOCAL du gate CI (dérivé de gate.needs, sans liste en dur).
Rejoue en une commande, sans runner ni réseau, l'ensemble de checks que le gate
de merge Gitea Actions agrège.
Usage :
bash run_ci.sh exécute tout gate.needs (miroir CI complet)
bash run_ci.sh --static seulement les gates statiques ci/*.sh (rapide)
bash run_ci.sh --list liste les jobs qui SERAIENT lancés, sans exécuter
bash run_ci.sh --help affiche cette aide et sort
Sortie : une ligne PASS/FAIL par job + un récapitulatif.
Code de sortie : 0 ssi tous les jobs passent · 1 si un job échoue ·
2 option inconnue · 3 hors checkout git / ci.yml illisible.
EOF
}
# --- aide : disponible AVANT toute résolution git (n'exige aucun checkout) ---
case "${1:-}" in -h|--help) usage; exit 0 ;; esac
# --- racine du dépôt (marche depuis n'importe quel sous-dossier) -------------
ROOT="$(git rev-parse --show-toplevel 2>/dev/null)" || {
echo "run_ci.sh : hors d'un checkout git — impossible de localiser ci.yml." >&2
exit 3
}
cd "$ROOT" || exit 3
CI_YML=".gitea/workflows/ci.yml"
[ -f "$CI_YML" ] || { echo "run_ci.sh : $CI_YML introuvable." >&2; exit 3; }
MODE="all"
case "${1:-}" in
--static) MODE="static" ;;
--list) MODE="list" ;;
"") MODE="all" ;;
*) echo "run_ci.sh : option inconnue « $1 » (attendu : --static | --list | --help)" >&2; exit 2 ;;
esac
# --- parse ci.yml : gate.needs (ordre) + par job {working-directory, run} ----
# Les commentaires de ligne sont retirés d'abord (un « working-directory: » ou
# « bash ci/… » cité en commentaire ne doit JAMAIS être capturé). Émet, sur
# stdout, une ligne par job de gate.needs : "<job>\t<wd|->\t<commande run>".
parse_jobs() {
awk '
{ line=$0; sub(/^[[:space:]]*#.*$/, "", line) } # drop full-line comments
/^jobs:[[:space:]]*$/ { inj=1 }
# début dun job : clé à exactement 2 espaces dindentation
inj && match(line, /^ ([a-z0-9_-]+):[[:space:]]*$/, m) { job=m[1]; next }
# gate.needs : la liste des jobs bloquants (source de vérité)
inj && job=="gate" && match(line, /needs:[[:space:]]*\[(.*)\]/, g) {
n=split(g[1], a, ","); for (i=1;i<=n;i++){ gsub(/[[:space:]]/,"",a[i]); if(a[i]!="") needs[++nn]=a[i] }
}
inj && job!="" && match(line, /^[[:space:]]+working-directory:[[:space:]]*(.+)$/, w) { wd[job]=w[1] }
# commande run: (avec un argument — écarte la clé « run: » nue de defaults)
inj && job!="" && match(line, /[[:space:]]run:[[:space:]]+(.+)$/, r) { cmd[job]=r[1] }
END {
for (i=1;i<=nn;i++){ j=needs[i]; printf "%s\t%s\t%s\n", j, (j in wd ? wd[j] : "-"), (j in cmd ? cmd[j] : "") }
}
' "$CI_YML"
}
mapfile -t ROWS < <(parse_jobs)
[ "${#ROWS[@]}" -gt 0 ] || { echo "run_ci.sh : gate.needs vide ou illisible dans $CI_YML." >&2; exit 3; }
is_static() { case "$1" in bash\ ci/*) return 0 ;; *) return 1 ;; esac; }
# --- décompte DÉRIVÉ (jamais figé) : gates statiques ci/*.sh vs suites module -
# Ventilé depuis les mêmes ROWS que l'exécution, donc toujours exact : ajouter
# un job à gate.needs le reclasse ici automatiquement, zéro édition (#6).
n_gates=0; n_suites=0
for row in "${ROWS[@]}"; do
IFS=$'\t' read -r _j _w _c <<<"$row"
[ -n "$_c" ] || continue
if is_static "$_c"; then n_gates=$((n_gates+1)); else n_suites=$((n_suites+1)); fi
done
n_jobs=$((n_gates + n_suites))
# --- --list : montre le plan, nexécute rien --------------------------------
if [ "$MODE" = "list" ]; then
printf '%-28s %-42s %s\n' "JOB" "WORKING-DIR" "RUN"
for row in "${ROWS[@]}"; do
IFS=$'\t' read -r job wd cmd <<<"$row"
printf '%-28s %-42s %s\n' "$job" "$wd" "$cmd"
done
printf -- '-- %d jobs dérivés de gate.needs : %d gates statiques ci/*.sh + %d suites de module.\n' \
"$n_jobs" "$n_gates" "$n_suites"
exit 0
fi
# --- exécution ---------------------------------------------------------------
LOGDIR="$(mktemp -d)"
trap 'rm -rf "$LOGDIR"' EXIT
pass=0; fail=0; skip=0; failed_jobs=()
echo "run_ci.sh · miroir du gate CI (mode=$MODE) · $(git rev-parse --short HEAD 2>/dev/null || echo '?')"
if [ "$MODE" = "static" ]; then
echo "gate.needs dérivé : $n_jobs jobs · mode --static → $n_gates gates ci/*.sh seuls ($n_suites suites de module ignorées)"
else
echo "gate.needs dérivé : $n_jobs jobs = $n_gates gates ci/*.sh + $n_suites suites de module"
fi
echo "------------------------------------------------------------------------"
for row in "${ROWS[@]}"; do
IFS=$'\t' read -r job wd cmd <<<"$row"
[ -n "$cmd" ] || { printf ' SKIP %-28s (aucune commande run: dans ci.yml)\n' "$job"; skip=$((skip+1)); continue; }
if [ "$MODE" = "static" ] && ! is_static "$cmd"; then continue; fi
log="$LOGDIR/$job.log"
if [ "$wd" = "-" ]; then dir="$ROOT"; else dir="$ROOT/$wd"; fi
if ( cd "$dir" && eval "$cmd" ) >"$log" 2>&1; then
printf ' PASS %-28s %s\n' "$job" "$cmd"; pass=$((pass+1))
else
printf ' FAIL %-28s %s\n' "$job" "$cmd"; fail=$((fail+1)); failed_jobs+=("$job")
sed 's/^/ | /' "$log" | tail -n 12
fi
done
echo "------------------------------------------------------------------------"
printf 'Récapitulatif : %d PASS · %d FAIL · %d SKIP\n' "$pass" "$fail" "$skip"
if [ "$fail" -ne 0 ]; then
printf 'Jobs en échec : %s\n' "$(IFS=', '; echo "${failed_jobs[*]}")"
exit 1
fi
echo "✅ Tous les checks du gate franchis en local."
exit 0