Compare commits

..

14 Commits

Author SHA1 Message Date
Claude Code DTP Worker 85a9ba7fca [DTP-Worker 20260801_160354] Auto exec · session 20260801_160354
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-01 16:18:55 +00:00
Claude Code DTP Worker 28e5e2145b [DTP-Worker] Sprint 8 · buffer · CRM/Commissions : gate d'IDENTITÉ des RÔLES PAYÉS — le « à quel rôle » du barème vendeurs — appartenance SINGLETON au portail ventes (séparation des pouvoirs · invariant #5) double-ancrée contrat rbac_50_roles.json ⨯ littéral README ⨯ portails_business · nom Frappe erpnext_role_name RÉSOLU == contrat (invariant #6, jamais figé en dur) · role_id ∈ contrat (jamais fantôme) · MANIFEST.roles_rbac_utilises recoupé — recomputés de commission_plan.json + contrat + MANIFEST byte-gatés 2026-08-01 15:44:53 +00:00
Claude Code DTP Worker 290bd2ec02 [DTP-Worker] Sprint 8 · buffer · DevOps/CI : gate d'IDENTITÉ des identifiants d'infrastructure VPS (IP 153.75.250.214 + conteneurs Docker erpnext-backend-1/erpnext-frontend-1) — recomputés de CLAUDE.md §VPS · direction copie→source sur toute doc .md du mandat (hors journaux) · enveloppe conteneur DÉRIVÉE des stems service (backend/frontend) et non du préfixe compose ⇒ un renommage erpnext-*→oto-* mord · migration serveur cascade sur les 10 copies
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 15:15:24 +00:00
Claude Code DTP Worker 9963279879 [DTP-Worker] Sprint 8 · buffer · Faisabilité/generator : ANCRAGE des 3 copies aval des 6 marqueurs canoniques (#9/#10) — l'oracle d'enforcement renderer._CANONICAL_MARKERS (que check_canonical parcourt) ⨯ les 2 docstrings scorer.py ⨯ l'oracle du test test_generator.py, recomputés de CLAUDE.md #9/#10 (réutilise exp)
Le bloc « paramètres canoniques » n'ancrait que model.py::CANONICAL + le README ;
son propre commentaire nommait pourtant le défaut (« marqueurs hardcodés dans le
test, une copie de plus jamais comparée à CLAUDE.md ») sans le fermer. Nouveau bloc :
helper _list_vs_want6 (set-diff EXACT absent/en-trop) pour les copies-LISTES
renderer._CANONICAL_MARKERS + oracle du test ; substring normalisé (USD+DOP==USD + DOP)
pour les 2 docstrings scorer. 4 morsures vérifiées dont CLAUDE.md #10 Cardnet→Azul
mordant SIMULTANÉMENT model+renderer+test+2 docstrings (valeur de l'ancrage) ;
restauré vert · 7 gates re-verts. ci/README.md + activity log mis à jour.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 14:42:30 +00:00
Claude Code DTP Worker 8667cdf4de [DTP-Worker] Sprint 8 · buffer · Fiscal/e-CF DGII : gate d'IDENTITÉ des ÉVÈNEMENTS D'ÉMISSION (« le cœur du livrable ») — ENSEMBLE émetteurs {réservation,contrat} ⨯ DISJONCTION des brouillons (jamais de facture sur brouillon) ⨯ base_field ⨯ rôle unique compta-fiscaliste-ecf double-ancré RBAC portail compta ⨯ FormaPago défaut 3/Tarjeta/Cardnet #10, recomputés d'ecf_plan.json
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 14:14:18 +00:00
Claude Code DTP Worker 915be3cb77 [DTP-Worker] Sprint 8 · buffer · CRM/Commissions : gate de la PROMESSE ANTI-INVENTION (#6) — la raison d'être du barème vendeurs
Aucun taux de commission n'étant documenté dans CLAUDE.md, le README §« Anti-invention » promet (README:22-23) « taux_pct: null + source: null + a_confirmer: true pour CHAQUE évènement » et l'invariant CLI #7 REFUSE tout taux_pct fourni SANS source. Le bloc cross-cohérence commissions ne gate QUE brouillons/champs de base ; le bloc racine QUE le compte de tests (« 25 »). RIEN ne confrontait cette promesse à l'artefact byte-gaté out/commission_plan.json.

Dérive la plus dommageable possible du module : qu'un taux_pct DEVIENNE non-null (taux INVENTÉ, ou valeur remplie sans source) pendant que le README affirme encore « tout null » → le calcul base × taux (commlib/finance.py) émettrait une commission fondée sur un chiffre inventé — l'invention EXACTE que le module promet d'écarter. Aucune suite tests/ (FONCTIONS de calcul, jamais la promesse README vs artefact) ne l'attrape.

Gate ajouté (bloc « CRM/Commissions · ANTI-INVENTION (#6) ») : recompute de l'état null/confirmé depuis l'artefact + (a) invariant #7 permanent (aucun taux_pct sans source) · (b) a_confirmer ⟺ taux_pct is null · (c) MANIFEST.counts recoupe le plan (taux_a_confirmer / evenements) · (d) la promesse #6 du README REFLÈTE l'artefact dans les DEUX SENS (tout-null ⇒ promesse présente ; taux confirmé ⇒ promesse retirée) — zéro faux négatif quand la Direction remplira les taux · (e) note_taux du MANIFEST cohérente.

5 morsures vérifiées (taux sans source · taux avec source mais a_confirmer stale + README périmé · a_confirmer=false sur null · counts périmé 5→4 · promesse retirée INTROUVABLE), restauré vert, 7 gates re-verts. Working tree byte-restauré via git checkout -- (JAMAIS git clean). Édition hors 05_deliverables_mvp/*/out ⇒ 0 dérive d'artefact. ci/README.md (récap + paragraphe détaillé) mis à jour. Auto-score 4Big 96/100.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 13:41:28 +00:00
Claude Code DTP Worker 2e627a6a3b [DTP-Worker] Sprint 8 · buffer · Fiches agents/master prompt : gate d'IDENTITÉ de l'endpoint GPU RunPod ComfyUI (pod t68akvx80wv092 + tag « Flux dev fp8 » · 6 citations BIM/Rendu/master prompt) ancré sur le bullet §1 de AGENTS_EXISTING_ASSETS.md
Surface data-derived jusqu'ici hors de tout gate : l'ID du pod RunPod ComfyUI
et le tag modèle Flux, recopiés à la main en 6 endroits, dont la source de
vérité in-repo est le fichier maître AGENTS_EXISTING_ASSETS.md (que les fiches
citent explicitement). Un pod re-créé (nouvel id) ou un modèle bumpé péримait
les citations en silence → batch Flux câblé vers un endpoint GPU mort (#6).

Gate « RunPod endpoint » (ci/check_readme_claims.sh, avant sys.exit) : id+tag
recomputés du bullet §1 (garde : bullet unique) ; direction fiche→inventaire —
toute fiche/master prompt mentionnant RunPod doit citer l'id EXACT (token
backtické de forme pod == canonique) + le tag EXACT ; disparition totale échoue.
6 morsures vérifiées (id change → 3 fiches cascade · tag bump → 3 · mistype → 1
· tag drop → 1 · id drop → incomplet · bullet supprimé → ancre morte) ; restauré
green · 7 gates re-verts.

ci/README.md (clause récap + paragraphe détaillé) + 05_activity_log mis à jour.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 13:11:39 +00:00
Claude Code DTP Worker c6428533a0 [DTP-Worker] Sprint 8 · buffer · Fiches agents/colonne « Entrée CLI » : gate d'IDENTITÉ des 16 jeux de sous-commandes documentés (03_agents/*/AGENT.md) recomputés PAR AST des subparsers.add_parser() réels des scripts
Classe fermée `agent-fiche-role-attrs-ungated` (sous-classe colonne CLI). La colonne
« Entrée CLI » des tables de livrables cite à la main le jeu de verbes de chaque module
(`publiciste.py parse|validate|generate|run`, `faisabilite_gen.py
score|scaffold|generate|batch`, …). Les blocs amont gatent déjà génériquement — pour
TOUTES les fiches — les colonnes « Tests » (vs regression_plan) et « Job CI » (vs
ci.yml), mais RIEN ne liait les VERBES écrits aux sous-commandes que le CLI EXPOSE.

Piège #6 : renommer/ajouter/retirer/réordonner un add_parser dans le .py (ou le mal
recopier dans la fiche) laisse la fiche documenter une surface d'invocation FANTÔME —
la doc d'identité qui dit à l'agent COMMENT lancer son livrable ment en silence, le CI
reste vert (le module tourne sous le vrai verbe), aucune suite tests/ ne l'attrape.

Gate ajoutée (ci/check_readme_claims.sh, bloc « colonne Entrée CLI ») : _cli_subcommands()
recompute via AST l'ensemble ORDONNÉ des subparsers.add_parser("<verbe>") ; script résolu
par le chemin de module de la MÊME ligne de table (lien …/<path>/README.md) + basename ;
égalité EXACTE ensemble ET ordre exigée ; `|` markdown échappés `\|` dé-échappés avant
découpe. 16 cellules gatées (6 fiches ; regex [a-z0-9_] capte audit_5d/4big_gen.py).

7 morsures vérifiées, restaurées vert : fiche renomme/ajoute/retire/réordonne un verbe
(4) · le vrai « vert trompeur » add_parser renommé dans le .py fiche inchangée (5) ·
nouveau add_parser non documenté (6) · script déplacé => INTROUVABLE (7). 6 gates
re-verts. ci/README.md catalogue mis à jour. Incident : un git checkout large a reverté
l'edit non-commité du gate ; ré-appliqué (git clean JAMAIS utilisé · ci-allow sur le log).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 12:44:43 +00:00
Claude Code DTP Worker 64238f6833 [DTP-Worker] Sprint 8 · buffer · Frontend Console/AGENT.md : gate d'IDENTITÉ de l'ENSEMBLE des portails métier (énumération L11 · double ancrage workspaces frontend ⨯ rbac portails_business) + exclusion nommée de la console technique plateforme (6ᵉ portail RBAC hors métier, L46)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 12:12:10 +00:00
Claude Code DTP Worker dce409f0f6 [DTP-Worker] Sprint 8 · buffer · RBAC/SPEC §4 : gate d'IDENTITÉ de la colonne « Entité » de la « Cartographie rôles ↔ entités » (7 entités nommées + Groupe consolidé) doublement ancré sur CLAUDE.md §Entités (descripteurs) ⨯ rbac_50_roles.json (ENSEMBLE des entite_principale)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 11:43:06 +00:00
Claude Code DTP Worker c4487fdcf7 [DTP-Worker] Sprint 8 · buffer · SEO/fiche agent : gate d'IDENTITÉ des 2 métriques data-derived (schema.org 10 nœuds · hreflang 10 pages FR/EN/ES) de 03_agents/seo/AGENT.md ancré sur seo_schema_org.json ⨯ seo_hreflang.json
Les blocs SEO schema.org/hreflang ne gataient que seo/README.md ; la fiche
restituait N nœuds (Organization + M Residence) et N pages × langues sans gate.
Recompute des memes artefacts byte-gates + coherence arithmetique total==1+M.
8 morsures verifiees, working tree byte-restaure, 7 gates re-verts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 11:10:08 +00:00
Claude Code DTP Worker 71999e04c4 [DTP-Worker] Sprint 8 · buffer · Frontend Console/AGENT.md : gate d'IDENTITÉ des configs runtime Chat OTOIA (persona · langues · endpoint:null #6/#8) ancré sur chat_mount.json ⨯ MANIFEST.json
Bloc anti-dérive #6 dans ci/check_readme_claims.sh : la cellule « Métrique
vérifiée » de 03_agents/frontend_console/AGENT.md:29 restituait 3 attributs
data-derived du montage OTOIA (persona Amélie · langues FR/EN/ES ·
`endpoint: null`) ; le bloc chat_otoia amont ne gate que le COMPTE
blocks/mounts/portails, et le gate persona/capabilities ancré sur CLAUDE.md
vise spec/mounts/MANIFEST/README/oracle, jamais cette fiche. Le nouveau bloc
recompute les 3 depuis out/chat_mount.json (5 configs) + out/MANIFEST.json
byte-gatés : persona==MANIFEST.persona.nom, langues==MANIFEST.langues majuscule,
endpoint:null == affirmation d'honnêteté exigeant all(mount.endpoint is None)
— un endpoint fabriqué mord. Cohérences croisées MANIFEST⇔chaque mount +
endpoint_statut=='a_confirmer'⇔endpoints tous null. 6 morsures vérifiées ·
7 gates verts · aucune écriture out/ (0 dérive artefact).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 10:40:20 +00:00
Claude Code DTP Worker 7e8bb9a0de [DTP-Worker] Sprint 8 · buffer · RBAC/SPEC §7 : gate d'IDENTITÉ des comptes du run-book d'application (28 UP templates · 6 Role Profiles / 5 métier · 50 rôles bijectifs) ancrés sur rbac_50_roles.json
Surface data-derived non gatée : la NOTE §7 «  … livré en-repo » de
RBAC_50_ROLES_SPEC.md restitue 3 comptes du run-book (28 templates Company /
6 profils (5 métier + plateforme technique) / 50 rôles bijectifs) sur une
surface DISTINCTE des READMEs userperm_gen/roleprofile_gen déjà gatés et du
§3 (per-portail). Nouveau bloc 1quinquies dans check_readme_claims.sh :
recompute depuis le contrat byte-gaté (|scope==entite|, |portails distincts|,
|métier hors plateforme|, |roles|), blockquote « > » neutralisé avant match.
6 morsures vérifiées (dont contrat entite→groupe = silent green), restauré
vert, 7 gates PASS. ci/README.md (récap + paragraphe détaillé) mis à jour.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 10:11:39 +00:00
Claude Code DTP Worker 7ce0f7b1e7 [DTP-Worker] Sprint 8 · buffer · RBAC/SPEC §3 : correction DÉFAUT FACTUEL (direction 10→9 · plateforme 5→6) + gate d'identité « Nb rôles » par portail ancré sur rbac_50_roles.json
La table §3 de RBAC_50_ROLES_SPEC.md avait dérivé du contrat en préservant le total
50 (10+5 vs 9+6) → invisible aux gates agrégés. Correction alignée sur la source +
nouveau bloc check_readme_claims recomptant chaque portail (et le total) depuis
rbac_50_roles.json. quality_report.json régénéré (evidence octets SPEC). 7 gates PASS.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 09:39:54 +00:00
5 changed files with 2274 additions and 4 deletions
+827
View File
@@ -1011,3 +1011,830 @@ absolu) · **7 gates re-verts** (`check_readme_claims`/`check_ci_integrity`/`che
édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact ; l'obtention édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact ; l'obtention
réelle des 7 confirmations auprès de leurs owners reste côté agent DevOps / VPS). réelle des 7 confirmations auprès de leurs owners reste côté agent DevOps / VPS).
- **Auto-score 4Big** : 96/100. - **Auto-score 4Big** : 96/100.
## Session `20260801_093301` · Buffer S8 · RBAC/RBAC_50_ROLES_SPEC §3 — DÉFAUT FACTUEL corrigé + gate d'identité par portail
**Contexte** : Sprint 8 · buffer (roadmap fonctionnellement close ; poursuite de la
série anti-dérive CLAUDE.md #6). Cette fois la surface data-derived non gatée était
**FAUSSE**, pas seulement à risque : la table §3 « Cartographie portails ↔ familles »
de `RBAC_50_ROLES_SPEC.md` (colonne « Nb rôles » PAR portail) avait dérivé du contrat
`rbac_50_roles.json` **en préservant le total 50**, donc en silence.
**Défaut factuel trouvé & corrigé** :
- `05_deliverables_mvp/rbac/RBAC_50_ROLES_SPEC.md:47` — portail `direction` affichait
**10** rôles ; le contrat en compte **9** (6 direction + CFO/finance + analyste
faisabilité + 2 legal).
- `…:52` — portail `plateforme` affichait **5** ; le contrat en compte **6** (devops ·
rbac-admin · qa · otoia · mobile · bi).
- L'erreur était compensée (10+5 vs 9+6) → total 50 exact → invisible aux gates
agrégés. Le MANIFEST roleprofile (byte-gaté) disait déjà 9 et 6 : la SPEC seule
avait dérivé. Corrigé pour refléter la source.
**Dérive silencieuse fermée (gate durable)** :
- `ci/check_readme_claims.sh` — nouveau bloc « 1quater) RBAC_50_ROLES_SPEC §3 » : RECOMPTE
le nombre de rôles PAR portail depuis `rbac/rbac_50_roles.json` (source de vérité,
byte-gatée par `check_artifacts`) et exige l'égalité avec CHAQUE ligne de la table §3
**plus le total** `**Total** | **50**`. Un portail absent de la table échoue aussi
(disparition = régression de traçabilité). Complète le gate roleprofile (qui gate le
MANIFEST, un doc distinct) sur une 2ᵉ surface transcrivant la même ventilation.
- Vérif négative : réintroduire `direction=10` fait échouer le gate (exit 1, message
« dit 10 MAIS rbac_50_roles.json compte 9 ») ; restauré → vert.
**Artefact régénéré (reproductibilité #artifact-reproducibility-gate)** :
- `qa/audit_4big/out/quality_report.json` — l'`evidence` DOC cite la taille en octets de
la SPEC (10040 → 10039 octets après correction 1 o) ; rebuild `audit_4big_gen.py build
-o out` byte-identique. `check_artifacts` re-vert.
**CI** : les 7 gates PASS (validate_json · check_artifacts · check_docs ·
check_readme_claims · guard_constraints · check_regression · check_ci_integrity).
- **Hors périmètre worker (VPS · #8)** : néant (correction d'un doc en-repo + gate
bash/python3 stdlib ; l'unique artefact `out/` touché a été régénéré à l'identique).
- **Auto-score 4Big** : 96/100.
## Session `20260801_100301` · Buffer S8 · RBAC/RBAC_50_ROLES_SPEC §7 — comptes du run-book d'application ancrés sur le contrat rbac_50_roles.json
**Contexte** : Sprint 8 · buffer (roadmap fonctionnellement close ; poursuite de la
série anti-dérive CLAUDE.md #6). Même classe que le §3 gaté à la session précédente
(`093301`, per-portail) : une **2ᵉ surface de la MÊME SPEC** transcrivant à la main des
comptes data-derived, déjà gatés ailleurs mais AVEUGLES à cette note (mémoire
[[rbac-spec-portail-count-gate]]).
**Dérive silencieuse fermée** :
- `05_deliverables_mvp/rbac/RBAC_50_ROLES_SPEC.md` §7 — la NOTE « ✅ … est **livré**
en-repo » restitue TROIS comptes du run-book d'application sur une **surface
DISTINCTE** des READMEs `userperm_gen` / `roleprofile_gen` (déjà gatés) et du §3
(ventilation par portail, bloc `1quater`) : « **28** templates `Company` (portée
`entite`) » · « **6 profils** (**5** portails métier + console technique
`plateforme`) » · « **50 rôles de façon bijective** ».
- Source faisant autorité : `rbac/rbac_50_roles.json` (contrat, byte-gaté par
`check_artifacts`). Recomputes : 28 == |`scope_donnees == entite`| (= nombre de
`User Permission` templates) · 6 == |portails distincts| (1 `Role Profile` par
portail) · 5 == portails **métier** (hors `plateforme`) · 50 == |rôles|.
- Piège #6 : BUMPER une portée `entite``groupe` (le décompte de templates change) ou
AJOUTER/RETIRER un portail (le décompte de profils change) dans le contrat périme la
note EN SILENCE pendant que les READMEs de volet et le §3 restent verts → l'agent
ERPNext Backend lirait un run-book promettant un décompte faux. Aucune suite `tests/`
(FONCTIONS de génération/résolution RBAC, jamais la prose de la SPEC) n'attrape ce
« vert trompeur ».
- **État courant** : aucun compte périmé — 28/6/5/50 recoupent le contrat exactement
(anti-invention #6, rien à réécrire). Le défaut est la **surface ungated**.
**Gate ajouté** (`ci/check_readme_claims.sh`, bloc « 1quinquies) RBAC_50_ROLES_SPEC §7 »
après le bloc §3) : réutilise `src_roles` (contrat) + `spec_rd` (déjà chargés par le
§3, zéro duplication). Le blockquote markdown `> ` de la note survit au collapse de
`read_norm` et fragmente la prose (« 28 > templates ») ⇒ **neutralisé** (`spec7 =
spec_rd.replace("> ", "")`) avant match. Exige : (a) « N templates `Company` (portée
`entite`) » == |scope==entite| ; (b) « **N profils** (M portails métier + console
technique `plateforme`) couvrant les **K rôles de façon bijective** » == |portails
distincts|/|métier hors plateforme|/|rôles|. Garde anti-typo : `plateforme` doit exister
au contrat (sinon la note « console technique plateforme » est fausse). Un claim absent
échoue AUSSI (INTROUVABLE = régression #6).
**6 morsures vérifiées** (restauré vert après chacune) : SPEC `28→27` (prose périmée) ·
SPEC `6 profils→7` · SPEC `5 portails métier→4` · SPEC `50→49` · **contrat** une portée
`entite→groupe` (28→27 · README stale = le vrai *silent green* · « SPEC dit 28 MAIS
compte 27 ») · SPEC retire « 28 templates » (claim absent = INTROUVABLE) ; restauré =
green : UP templates 28 == |entite| · Role Profiles 6 (métier 5) bijectifs sur 50 ==
contrat · 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é « Comptes
du run-book d'application `RBAC_50_ROLES_SPEC.md` §7 ») mis à jour.
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ;
aucune écriture dans un `out/` ⇒ 0 dérive d'artefact ; l'application `bench` réelle
reste côté agent ERPNext Backend / VPS).
- **Auto-score 4Big** : 96/100.
## Session `20260801_103304` · Buffer S8 · Chat OTOIA — IDENTITÉ des configs runtime (persona · langues · endpoint) dans la fiche Frontend Console
**Contexte** : Sprint 8 · buffer (roadmap fonctionnellement close ; poursuite de la
série anti-dérive CLAUDE.md #6, classe « fiche agent qui restitue des attributs
data-derived, gatée uniquement sur des COMPTES agrégés » — mémoire
[[agent-fiche-role-attrs-ungated]]).
**Dérive silencieuse fermée** :
- `03_agents/frontend_console/AGENT.md:29` — la cellule « Métrique vérifiée » du
livrable `chat_otoia` restitue TROIS attributs data-derived du montage OTOIA :
« persona **Amélie** … · langues **FR/EN/ES** · `` `endpoint: null` `` ». Le bloc
chat_otoia amont (`ci/check_readme_claims.sh`) ne recompute que le **COMPTE**
blocks/mounts/portails (tous = 5) — AVEUGLE à CE QUE dit chaque config runtime.
- Le gate persona/capabilities « ancré sur CLAUDE.md §Architecture cible » vise le
spec, les 5 mounts, le MANIFEST, le README et l'oracle du test — **JAMAIS** cette
cellule de fiche, et il ignore langues comme endpoint. La cellule était donc une
surface data-derived ungated de plus.
- Sources faisant autorité (byte-gatées par `check_artifacts`) :
`frontend/chat_otoia/out/chat_mount.json` (une config par portail · 5) +
`out/MANIFEST.json` (persona/langues/endpoint_statut consolidés).
- **`endpoint: null` = AFFIRMATION D'HONNÊTETÉ #6/#8** : l'endpoint OTOIA n'est PAS
fabriqué (renseigné côté VPS · agent Frontend). Qu'une config gagne un endpoint réel
dans l'artefact et la fiche « endpoint: null » devient un MENSONGE en silence.
- **État courant** : aucun attribut périmé — persona `Amélie` · langues `fr/en/es` ·
`endpoint == null` sur les 5 mounts ET le MANIFEST (anti-invention #6, rien à
réécrire). Le défaut est la **surface ungated**.
**Gate ajouté** (`ci/check_readme_claims.sh`, bloc « Chat OTOIA · IDENTITÉ des configs
runtime (persona · langues · endpoint) » après le bloc de COMPTE chat_otoia) : charge
`MANIFEST.json` + `chat_mount.json` (5 configs) et exige, sur la cellule de la fiche
(regex `persona (\S+) QC · langues ([A-Z/]+) · \`endpoint: ([^\`]+)\``, ne matche QUE
la ligne :29, pas la prose Scope :12) : (a) persona == `MANIFEST.persona.nom` (le
« QC » est le qualificatif de marque `Voix Amélie QC` de CLAUDE.md, gardé en littéral
NON gaté) ; (b) langues `FR/EN/ES` == `MANIFEST.langues` en majuscule ; (c) `endpoint:
null` avec `all(m.endpoint is None)` sur les 5 mounts (sinon `exp_endpoint` devient
`NON-null(...)` ⇒ morsure d'honnêteté). Cohérences croisées : `MANIFEST.persona.nom`
et `MANIFEST.langues` == CHAQUE mount (montage désynchronisé mordu) ·
`endpoint_statut == 'a_confirmer'` ⇔ endpoints TOUS null (incohérence interne
artefact sinon). Un claim absent échoue AUSSI (INTROUVABLE = régression #6). Aucune
suite `tests/` (FONCTIONS du builder, jamais la prose de la fiche) n'attrape ce
« vert trompeur ».
**6 morsures vérifiées** (restauré vert après chacune) : fiche persona `Amélie→Amelia`
(mismatch) · fiche langues `FR/EN/ES→FR/EN` · fiche `endpoint: null→endpoint: fixé` ·
**artefact** un mount `endpoint null→URL réelle` (bascule d'honnêteté ET incohérence
`endpoint_statut='a_confirmer'` — le vrai *silent green* : l'endpoint fabriqué pendant
que la fiche promet `null`) · fiche retire le bloc attributs (claim absent =
INTROUVABLE) · **artefact** un mount `persona Amélie→Nadia` (montage désynchronisé du
MANIFEST). Working tree byte-restauré (`git checkout --`, **JAMAIS** `git clean`,
interdit absolu) · **7 gates re-verts** (`check_readme_claims`/`check_artifacts`/
`check_docs`/`guard_constraints`/`check_regression`/`check_ci_integrity`/`validate_json`).
- `ci/README.md` (clause de la table récap du pipeline + paragraphe détaillé sous le
bloc de COMPTE chat_otoia, avec renvoi au gate persona/capabilities distinct) mis à
jour.
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ;
aucune écriture dans un `out/` ⇒ 0 dérive d'artefact ; le câblage réel de l'endpoint
OTOIA + web-component reste côté agent Frontend / VPS).
- **Auto-score 4Big** : 96/100.
## Session `20260801_110311` · Buffer S8 · SEO — IDENTITÉ des 2 métriques data-derived (schema.org · hreflang) de la fiche agent
**Contexte** : Sprint 8 · buffer (roadmap fonctionnellement close ; poursuite de la
série anti-dérive CLAUDE.md #6, classe « fiche agent qui restitue des attributs
data-derived, gatée uniquement en AGRÉGAT » — mémoire
[[agent-fiche-role-attrs-ungated]] ; même patron que la cellule chat_otoia de la
fiche Frontend Console traitée à la session précédente).
**Dérive silencieuse fermée** :
- `03_agents/seo/AGENT.md:25-26` — le tableau « Livrable réellement produit »
restitue DEUX sorties byte-gatées du bundle SEO à côté du compte de mots-clés
(lui déjà gaté sur README ET fiche) :
- L25 : « graphe JSON-LD : **10 nœuds** (`Organization` + 9 `Residence`) »
- L26 : « **10 pages** × FR/EN/ES + `x-default` »
- Les blocs SEO schema.org (`SEO_SG_RD`) et hreflang (`SEO_HL_RD`) amont ne lisent
QUE `seo/README.md` — JAMAIS la fiche. `SEO_FI` n'était utilisé que par le bloc
de COMPTE de mots-clés. Ces deux cellules étaient donc des surfaces data-derived
ungated : RENOMMER le `@type` des listings, AJOUTER/RETIRER un projet (→ N nœuds
& N pages dérivent) ou ÉCHANGER une langue de contenu périme la fiche pendant que
les trois artefacts byte-gatés (`seo_schema_org.json`/`MANIFEST.json` et
`seo_hreflang.json`) disent autre chose — « vert trompeur » qu'aucune suite
`tests/` (FONCTIONS du builder, jamais la prose de la fiche) n'attrape.
- **État courant** : aucune métrique périmée — 10 nœuds (1 `Organization` + 9
`Residence`) · 10 pages · langues `fr/en/es` + `x-default` (anti-invention #6,
rien à réécrire). Le défaut est la **surface ungated**.
**Gate ajouté** (`ci/check_readme_claims.sh`, bloc « SEO · FICHE AGENT » inséré
après le bloc hreflang README, réutilise les vérités déjà recomputées par les blocs
schema.org/hreflang amont — zéro duplication #6) :
- L25 (regex `graphe JSON-LD : \*\*(\d+) nœuds\*\* \(`<racine>` \+ (\d+)
`<listing>`\)`) : total == `schema_org_nodes` · racine == `@type` singleton du
graphe · M == `listings` · type listing == `@type` uniforme des listings ·
cohérence arithmétique `total == 1 + M`.
- L26 (regex `\*\*(\d+) pages\*\* × ([A-Z/]+) \+ `x-default``) : `|pages|` == carte
hreflang · jeu de langues (lower) == langues de contenu de l'artefact (set-diff :
absent ET en trop).
- Un claim absent échoue AUSSI (INTROUVABLE = régression #6).
**8 morsures vérifiées** (restauré vert après chacune) : nœuds `10→11` · listing
`Residence→Apartment` · racine `Organization→LocalBusiness` · listings `9→8`
(casse l'arithmétique ET la bijection) · pages `10→12` · langue `es→pt` · retrait
`es` (`FR/EN`) · retrait du claim L25 (INTROUVABLE). Working tree byte-restauré
(`git checkout --`, **JAMAIS** `git clean`, interdit absolu) · **7 gates re-verts**
(`check_readme_claims`/`check_artifacts`/`check_docs`/`guard_constraints`/
`check_regression`/`check_ci_integrity`/`validate_json`).
- `ci/README.md` (clause de la table récap du pipeline + paragraphe détaillé « Fiche
agent SEO — 2 métriques data-derived » après le paragraphe hreflang) mis à jour.
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ;
aucune écriture dans un `out/` ⇒ 0 dérive d'artefact ; l'injection réelle des
balises hreflang/JSON-LD dans les pages reste côté agent Frontend/SEO / VPS).
- **Auto-score 4Big** : 96/100.
## Session `20260801_113314` · Buffer S8 · RBAC/RBAC_50_ROLES_SPEC §4 — colonne « Entité » de la « Cartographie rôles ↔ entités » ancrée sur CLAUDE.md §Entités rbac_50_roles.json
**Contexte** : Sprint 8 · buffer (roadmap fonctionnellement close ; poursuite de la
série anti-dérive CLAUDE.md #6, double classe « ANCRAGE à CLAUDE.md »
([[claude-md-constant-anchor-gate]]) **et** « identité d'ensemble recomputée du
contrat RBAC » [[rbac-spec-portail-count-gate]]). Même patron que les entités CONFOTUR
`entite_porteuse` (session 053228, §Entités) et les comptes SPEC §3/§7 — appliqué à
une **section encore ungated de la MÊME SPEC** : la table §4.
**Dérive silencieuse fermée** :
- `05_deliverables_mvp/rbac/RBAC_50_ROLES_SPEC.md` §4 « Cartographie rôles ↔ entités » —
la **colonne de tête** liste les entités porteuses (`entite_principale`) : les **7
entités NOMMÉES** du mandat (`WAF` (holding) · `WA SRL` · `AC Arias Cuevas` ·
`Consortium ECR DR` (paymaster RH) · `Helios RD` (marque publique) · `Ploutos`
(patrimoine/monétaire) · `9060 QC` (World Activities Cdn.)) + le périmètre consolidé
« **Groupe** (consolidé) ».
- **Deux sources faisant autorité** : (i) **CLAUDE.md §Entités** — l'orthographe + le
descripteur parenthétique de chaque entité nommée ; (ii) `rbac_50_roles.json`
(byte-gaté via les 3 MANIFEST) — l'ENSEMBLE des `entite_principale` réellement portées
par les 50 rôles (distinct = 11 Helios RD · 11 WA SRL · 10 AC Arias Cuevas · 8 Groupe ·
5 9060 QC · 3 WAF · 1 Ploutos · 1 Consortium ECR DR = 50).
- Piège : le §3 gate les comptes par **portail**, le §7 les comptes du run-book, les
blocs fiches l'`entite` d'**UN** rôle ; tous **aveugles à l'identité de CETTE liste**.
AJOUTER au contrat un rôle porté par une entité absente de §4 (menu périmé), RENOMMER
une entité dans CLAUDE.md §Entités (`9060 QC→9061 QC`) ou DÉRIVER un descripteur laisse
la SPEC contredire le mandat/le contrat EN SILENCE — « vert trompeur » qu'aucune suite
`tests/` (FONCTIONS RBAC, jamais la prose de la SPEC) n'attrape.
- **État courant** : aucune ligne périmée — §4 == distinct contrat (8 entités) · les 7
descripteurs == CLAUDE.md §Entités · aucune entité décorative (anti-invention #6, rien
à réécrire). Le défaut est la **surface ungated**.
**Gate ajouté** (`ci/check_readme_claims.sh`, bloc `1sexies` après le §7, réutilise
`src_roles`/`spec_rd`/`SPEC_RD` déjà en scope · extraction fraîche de CLAUDE.md §Entités
— `_nfc` défini plus bas, non requis, entités ASCII) :
- **(a) ENSEMBLE** — entités de la colonne §4 (`| **Entité** (desc)? |`) == distinct
`entite_principale` du contrat (set-diff : `manquantes` = portées mais hors §4 ·
`fantômes` = listées mais jamais portées).
- **(b) ANCRAGE** — chaque entité NOMMÉE de CLAUDE.md §Entités (tokens en gras) est une
ligne §4 ET son descripteur parenthétique == CLAUDE.md **EXACT** (absence comprise :
`WA SRL`/`AC Arias Cuevas` = sans descripteur des deux côtés).
- **(c)** chaque entité nommée du mandat est PORTÉE ≥1× par le contrat (`cm_ents ⊆
contrat`, aucune décorative).
- Section `## 4.` INTROUVABLE = claim absent = régression #6.
**8 morsures vérifiées** (4 côté contrat/CLAUDE.md · 4 côté SPEC) : contrat +rôle porté
par entité `Fantasia` absente de §4 (`(a)` manquante) · contrat `Ploutos→Ploutos SRL`
sur tous les rôles (`(a)` fantôme+manquante · `(c)` `Ploutos` décorative) · CLAUDE.md
`9060 QC→9061 QC` (`(b)` absent · `(c)` décorative) · CLAUDE.md descripteur
`(holding)→(holding co)` (`(b)` mismatch) · SPEC §4 `AC Arias Cuevas→AC Arias` (`(a)`
fantôme+manquante · `(b)` absent) · SPEC §4 descripteur `marque publique→marque privée`
(`(b)` mismatch) · SPEC §4 retire la ligne « Groupe » (`(a)` manquante) · SPEC supprime
l'en-tête `## 4.` (INTROUVABLE) ; restauré = green : 9 ✓ (`(a)` 8 entités · `(b)` 7
descripteurs · `(c)` 7 portées) · exit 0. Working tree byte-restauré (`git checkout --`,
**JAMAIS** `git clean`, interdit absolu) · **7 gates re-verts** (`check_readme_claims`/
`check_artifacts`/`check_docs`/`guard_constraints`/`check_regression`/
`check_ci_integrity`/`validate_json`).
- `ci/README.md` (clause de la ligne récap du pipeline + paragraphe détaillé « Colonne
Entité de la §4 ancrée sur CLAUDE.md §Entités ») 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 ; le seed réel des
rôles/entités `bench` reste côté VPS).
- **Auto-score 4Big** : 96/100.
## Session `20260801_120319` · Buffer S8 · Frontend Console/AGENT.md — IDENTITÉ de l'ENSEMBLE des portails métier + exclusion nommée de la console technique
**Contexte** : Sprint 8 · buffer (roadmap fonctionnellement close ; poursuite de la
série anti-dérive CLAUDE.md #6 — fiche-agent qui restitue une donnée gatée seulement
en AGRÉGAT/COMPTE, cf. [[agent-fiche-role-attrs-ungated]]). Même patron que
l'énumération ordonnée des onglets Mobile (`role_navigation`) et la colonne « Entité »
de la SPEC §4 ([[rbac-spec-portail-count-gate]]) — appliqué à une surface encore ungated
de la fiche **Frontend Console**.
**Dérive silencieuse fermée** :
- `03_agents/frontend_console/AGENT.md` restitue à la main, en **deux endroits**
data-derived, l'IDENTITÉ de l'ensemble des portails — jamais gatée hors le COMPTE :
- **L11** « **5 portails rôle métier** (Ventes / Construction / Achat / Compta /
Direction) » — l'ENSEMBLE ORDONNÉ des noms courts.
- **L46** « la console technique `plateforme` (**6ᵉ portail RBAC**) est **exclue** »
— l'identité + le rang + la non-appartenance au métier de la console exclue.
- **Deux artefacts faisant autorité** : (i) `frontend/portails/out/MANIFEST.json`
(byte-gaté) — les workspaces métier (`OTO Ventes` … → noms courts sans le préfixe
« OTO ») ; (ii) `rbac_50_roles.json` — `portails_business` (l'ordre du contrat) + les
portails distincts des rôles (6 : 5 métier + `plateforme`).
- Piège : le bloc « triplets par workspace » recompute cartes/liens/rôles **par**
portail mais reste **aveugle à QUELS portails** composent l'ensemble ; les autres
gates comptent « 5 » sans jamais confronter le SET. Un ÉCHANGE (Achat→Marketing),
RETRAIT, RÉORDRE, RENOMMAGE de la console technique dans le contrat
(`plateforme`→`console`) ou un contrat qui PROMEUT `plateforme` en métier laisse la
fiche contredire les artefacts EN SILENCE — « vert trompeur » qu'aucune suite
`tests/` (FONCTIONS RBAC, jamais la prose) n'attrape.
- **État courant** : aucune dérive — L11 == workspaces frontend == `portails_business` ·
L46 `plateforme` == unique portail RBAC hors métier == 6ᵉ. Le défaut est la **surface
ungated**.
**Gate ajouté** (`ci/check_readme_claims.sh`, bloc « Portails ensemble » juste après le
bloc triplets, réutilise `ws_list`/`ws_fi`/`WS_FI` déjà en scope · relit le contrat
`rbac_50_roles.json`) :
- **(a) ENSEMBLE** — noms courts ordonnés (label `MANIFEST.workspaces[].workspace` sans
« OTO ») == énumération L11 (compte + absents/en trop + ordre) **ET DOUBLE ANCRAGE**
les ids workspaces frontend == `portails_business` du contrat (l'« égalité stricte »
que la fiche revendique en L47).
- **(b) EXCLUSION** — `plateforme` == l'UNIQUE portail RBAC hors métier
(`distinct(roles.portail) portails_business`, len==1), nommé EXACTEMENT dans L46,
de rang == `|distinct|` (6ᵉ), avec `**exclue**` structurellement exigé (regex `\S*`
absorbe l'exposant « ᵉ » multi-octets sans le coder en dur).
- Claim absent (L11 ou L46 INTROUVABLE) = régression #6.
**8 morsures vérifiées** (6 côté fiche · 2 côté contrat) : L11 `Achat→Marketing`
(`(a)` en trop/absent) · L11 retire `Compta` (`(a)` compte+absent) · L11 réordonne
`Ventes↔Construction` (`(a)` ordre) · L46 `plateforme→console` (`(b)` nom) · L46
`6ᵉ→7ᵉ` (`(b)` rang) · L46 retire `**exclue**` (INTROUVABLE) · contrat réordonne
`portails_business` (ancrage rompu) · contrat `ventes→sales` dans `portails_business`
(ancrage rompu + `plateforme` n'est plus l'unique hors-métier ⇒ 2 fails) ; restauré =
green (3 ✓ : double ancrage · énumération L11 · console technique L46) · exit 0.
Working tree byte-restauré (`git checkout --`, **JAMAIS** `git clean`, interdit absolu) ·
**7 gates re-verts** (`check_readme_claims`/`check_artifacts`/`check_docs`/
`guard_constraints`/`check_regression`/`check_ci_integrity`/`validate_json`).
- `ci/README.md` (clause de la ligne récap du pipeline + paragraphe détaillé « Ensemble
des portails métier + exclusion nommée de la console technique ») 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 ; le seed réel des
Workspaces/rôles `bench` reste côté VPS).
- **Auto-score 4Big** : 96/100.
---
## Sprint 8 · buffer · Gate d'IDENTITÉ de la colonne « Entrée CLI » des fiches agents — verbes documentés == subparsers argparse réels
**Classe fermée** : `agent-fiche-role-attrs-ungated` (mémoire) — sous-classe « colonne
CLI ». Chaque table de livrables des fiches `03_agents/*/AGENT.md` porte une colonne
« Entrée CLI » citant, entre backticks, le point d'entrée du module ET son jeu de
verbes (`publiciste.py parse|validate|generate|run`, `faisabilite_gen.py
score|scaffold|generate|batch`, `rbac_apply_plan.py build|validate`, …). Cette liste
est **saisie à la main**. Les gates amont couvrent déjà génériquement — pour TOUTES les
fiches, via `git ls-files` — la colonne « Tests » (recomputée depuis `regression_plan`)
et la colonne « Job CI » (contre `ci.yml`), mais **RIEN** ne vérifiait que les VERBES
écrits sont ceux que le CLI **expose réellement**.
**Piège** : renommer une sous-commande (`generate`→`render`), en AJOUTER une
(`publish`), en RETIRER une ou les RÉORDONNER laisse la fiche documenter une surface
d'invocation **fantôme** : le module tourne (le CI appelle le vrai verbe), la doc
d'identité de l'agent — celle qui lui dit COMMENT lancer son propre livrable — ment en
silence. Aucune suite `tests/` (FONCTIONS du module, jamais la prose de fiche) ne
l'attrape. Classe explicitement notée en mémoire.
**Gate ajoutée** (`ci/check_readme_claims.sh`, bloc « colonne Entrée CLI », après le
bloc « Job CI fiches ») : `_cli_subcommands()` **RECOMPUTE** via **AST** l'ensemble
ORDONNÉ des `subparsers.add_parser("<verbe>")` de chaque script (jamais une liste à la
main · #6). Résolution du script : chemin du module de la MÊME ligne de table (lien
`…/05_deliverables_mvp/<path>/README.md`) + basename du CLI → `{D}/<path>/<file>.py`
(chaque cellule liée à SON module · pas de collision de basename). Égalité EXIGÉE
**exacte** — ensemble ET ordre (l'ordre de `add_parser` == ordre de l'aide argparse).
Les `|` séparateurs markdown sont échappés `\|` dans les cellules : dé-échappés avant
découpe. Direction fiche→CLI : la fiche ne peut pas mentir sur les verbes du script.
**16 cellules** gatées (6 fiches : crm×3 · devops · erpnext_backend×5 · faisabilite×2 ·
publiciste · qa×4 — dont `audit_5d_gen.py`/`audit_4big_gen.py` que les regex `[a-z_]`
manquaient, d'où `[a-z0-9_]`). Actuellement **toutes exactes** — la gate verrouille
l'état correct.
**7 morsures vérifiées** (restaurées vert après chaque) :
1. FICHE renomme verbe (`generate→render`) → manquants/en trop, `seul_ordre_diffère=False`.
2. FICHE ajoute verbe fantôme (`publish`) → en trop.
3. FICHE retire un verbe (`batch`) → manquant.
4. FICHE réordonne (`parse|validate`↔) → `seul_ordre_diffère=True` (ordre discriminé).
5. **Le vrai « vert trompeur »** : `add_parser("generate")→("render")` dans le `.py`,
fiche INCHANGÉE → red (la source de vérité a bougé sous la fiche).
6. Nouveau `add_parser("audit")` dans le `.py` non documenté → manquant côté fiche.
7. Script déplacé/renommé (`publiciste.py→publiciste_v2.py`) → « script INTROUVABLE ».
**Incident process** : un `git checkout .` de nettoyage d'un scénario adverse a reverté
l'edit **non-commité** de `ci/check_readme_claims.sh` (bloc effacé). Ré-appliqué à
l'identique puis re-vérifié. Leçon : ne jamais `git checkout .`/`--` large avec un edit
de gate non commité en cours ; restaurer par chemin ciblé uniquement. (`git clean` <!-- ci-allow : mention de l'interdit absolu, jamais exécuté -->
reste l'interdit absolu — non utilisé.)
- **6 gates re-verts** : `check_artifacts` · `check_docs` · `guard_constraints` ·
`check_readme_claims` (16 ✓ Entrée CLI) · `check_regression` · `check_ci_integrity`.
Aucun hash-lock sur les scripts de gate → rien d'autre à régénérer.
- **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_130325` · Buffer S8 · Fiches agents + master prompt/infra GPU : l'**IDENTITÉ de l'endpoint RunPod ComfyUI** — l'ID du pod `t68akvx80wv092` + le tag modèle **« Flux dev fp8 »** qui exécute le rendu Flux — était RECOPIÉE À LA MAIN en **6 citations** (`03_agents/bim/AGENT.md:32,67` · `03_agents/rendu/AGENT.md:19,55,91` · `02_master_prompt/MASTER_PROMPT_DTP_v2.md:24`) mais **HORS de tout gate**. La source de vérité in-repo est le bullet §1 de `AGENTS_EXISTING_ASSETS.md:15` — « le fichier maître » que les fiches CITENT explicitement comme leur ancre.
**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 de « vert
trompeur » que la colonne « Job CI » ou « Entrée CLI » des fiches (surface data-derived
recopiée à la main, jamais confrontée à sa source) — appliquée à une **surface distincte
encore jamais gatée** : l'**infra GPU** (l'endpoint RunPod que l'agent Rendu/BIM câble
pour le batch Flux). Classe `agent-fiche-role-attrs-ungated`.
**Dérive silencieuse fermée** :
- `03_agents/bim/AGENT.md` (table modules L32 + coordination L67) · `03_agents/rendu/AGENT.md`
(principe L19 + table L55 + coordination L91) · `02_master_prompt/MASTER_PROMPT_DTP_v2.md:24`
(bloc infra « GPU ») — chacun NOMME entre backticks l'id `t68akvx80wv092` et/ou le tag
« Flux dev fp8 ».
- Source faisant autorité : `AGENTS_EXISTING_ASSETS.md:15` §1 BIM
(« - **RunPod ComfyUI** endpoint `t68akvx80wv092` (Flux dev fp8) »).
- Piège : un pod RunPod est **RE-CRÉÉ avec un NOUVEL id** (cas le plus courant) ou le
modèle Flux est bumpé (`dev fp8`→autre quant) : l'inventaire maître se met à jour, les
6 citations se PÉRIMENT en silence pendant que l'agent Rendu/BIM câblerait le batch vers
un **endpoint GPU MORT** — l'invention même que #6 interdit. `check_docs` ne voit qu'un
lien, `guard_constraints` qu'un terme interdit, `check_artifacts` qu'un `out/` — un ID de
pod n'est aucun des trois ; aucune suite `tests/` (FONCTIONS du module) n'attrape la prose.
- **État courant** : **aucune citation périmée** — les 6 recoupent l'inventaire exactement
(anti-invention #6, rien à réécrire). Le défaut est la **surface ungated**.
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « RunPod endpoint » avant
`sys.exit`) : (0) **ancrage** — id + tag recomputés du bullet §1 (garde : bullet EXACTEMENT
une fois, sinon ancre morte) ; (1) **direction fiche→inventaire** — toute fiche/master
prompt qui MENTIONNE RunPod doit citer l'id EXACT (tout token backtické de forme pod —
alphanum minuscule ≥12 **avec un chiffre**, forme qui n'entre en collision avec aucun autre
token backtické du corpus : vérifié) == canonique, ET le tag Flux EXACT (substring
normalisé, insensible casse) ; (2) **disparition totale** — plus aucune mention RunPod
échoue AUSSI (traçabilité #6).
**6 morsures vérifiées** : inventaire re-crée le pod (id change → mord les **3 fiches** en
cascade) · inventaire bump le tag (`dev fp8→schnell fp8` → 3 fiches) · fiche rendu mistype
une citation (id fantôme, mord rendu seul) · fiche bim retire le tag (tag absent) · master
prompt retire l'id backtické (citation incomplète) · inventaire supprime le bullet canonique
(ancre morte, recompute impossible) ; restauré = green : 3 fiches · id == inventaire · tag ==
inventaire · exit 0. Working tree byte-restauré (`git checkout --`, **JAMAIS** `git clean`,
interdit absolu) · **7 gates re-verts** (`check_readme_claims`/`check_docs`/`guard_constraints`/
`check_artifacts`/`check_ci_integrity`/`validate_json`/`check_regression`).
- `ci/README.md` (clause récap du pipeline `check-readme-claims` + paragraphe détaillé
« IDENTITÉ de l'endpoint GPU RunPod ComfyUI ancrée sur l'inventaire maître ») 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 ; le pod RunPod réel + le
batch ComfyUI Flux restent côté VPS/RunPod).
- **Auto-score 4Big** : 96/100.
## Session `20260801_133334` · Buffer S8 · Domaine CRM/Commissions : la **PROMESSE ANTI-INVENTION (#6)** du barème vendeurs — la **RAISON D'ÊTRE** du module — était HORS de tout gate. Aucun taux de commission n'étant documenté dans CLAUDE.md, le README §« Anti-invention » promet (README:22-23) que « le barème livré porte `taux_pct: null` + `source: null` + `a_confirmer: true` pour **chaque** évènement », et l'invariant CLI #7 REFUSE tout `taux_pct` fourni **sans `source`**. Le bloc cross-cohérence commissions existant ne gate QUE brouillons/champs de base ; le bloc racine QUE le compte de tests (« 25 »). RIEN ne confrontait cette promesse à l'artefact byte-gaté.
**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 surface `crm/commissions`
que le bloc cross-cohérence (états soumis/base_field) mais **axe orthogonal et plus
grave** : non plus QUEL état paie, mais la garantie que le module **n'invente aucun
chiffre** — la classe même que #6 pose, appliquée au livrable dont c'est la mission.
**Dérive silencieuse fermée** :
- `05_deliverables_mvp/crm/commissions/README.md:22-23` — la promesse « `taux_pct:
null` + `source: null` + `a_confirmer: true` pour **chaque** évènement ».
- Source faisant autorité : `out/commission_plan.json` (byte-gaté par
`check_artifacts`) — chaque `evenements[]` porte `taux_pct`/`source`/`a_confirmer` ;
`out/MANIFEST.json[counts]` recompte `taux_a_confirmer`/`evenements`.
- Piège #6, la dérive la plus dommageable possible ici : qu'un `taux_pct` DEVIENNE
non-null (taux INVENTÉ, ou valeur remplie **sans `source`**) pendant que le README
affirme encore « tout `null` » → le calcul `base × taux` (`commlib/finance.py`)
émettrait une commission fondée sur un chiffre inventé — l'invention EXACTE que le
module promet d'écarter. Aucune suite `tests/` (FONCTIONS de calcul/résolution,
jamais la promesse du README vs l'artefact commité) ne l'attrape.
- **État courant** : les 5 évènements sont bien tout-`null` (`taux_pct`/`source`
null · `a_confirmer` true), `counts.taux_a_confirmer == 5 == |plan|`, `note_taux`
cohérente ; rien de périmé (anti-invention #6, rien à réécrire). Le défaut est la
**surface ungated**.
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « CRM/Commissions ·
ANTI-INVENTION (#6) » après le bloc cross-cohérence commissions) : on RECOMPUTE
l'état null/confirmé depuis l'artefact, puis (a) **invariant #7 permanent** — aucun
`taux_pct` renseigné **sans `source`** (taux inventé) ; (b) **cohérence** `a_confirmer`
⟺ `taux_pct is null` (un taux confirmé n'est plus « à confirmer », et inversement) ;
(c) **`MANIFEST.counts`** recoupe le plan (`taux_a_confirmer` == nb à confirmer ·
`evenements` == |plan|) ; (d) **la promesse #6 du README REFLÈTE l'artefact dans les
DEUX SENS** — si tout-null, la promesse DOIT être présente (sinon la raison d'être
disparaît · #6) ; si un taux est un jour confirmé, la promesse « tout null » DOIT
avoir été retirée (sinon « vert trompeur ») — **zéro faux négatif** quand la
Direction remplira les taux ; (e) `note_taux` du MANIFEST cohérente avec l'état.
**5 morsures vérifiées** : `taux_pct` rempli SANS source (invariant #7 · invention
+ cascade b/d) · `taux_pct` rempli AVEC source (invariant #7 OK mais `a_confirmer`
resté true → b, + README périmé → d — prouve que #7 et la cohérence sont distincts) ·
`a_confirmer=false` sur taux null (b + counts périmés c + d) · `MANIFEST.counts.
taux_a_confirmer` périmé 5→4 (c seul) · promesse anti-invention retirée du README
(d · INTROUVABLE) ; restauré = green : invariant #7 · cohérence · counts · promesse
== artefact · 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` (cellule récap du pipeline + paragraphe détaillé « promesse
anti-invention #6 du barème `crm/commissions` ») 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
confirmation réelle des taux + le câblage du calcul restent côté Direction/VPS).
- **Auto-score 4Big** : 96/100.
## Session `20260801_140334` · Buffer S8 · Domaine Fiscal/e-CF DGII : l'**IDENTITÉ des ÉVÈNEMENTS D'ÉMISSION** — la section §Cross-cohérence présentée comme **« le cœur du livrable »** (README:40-59) — était transcrite À LA MAIN SANS aucun gate d'identité. Le SEUL gate fiscal existant couvre le **contrat de format e-NCF** (`E+tipo(2)+seq(10)=13`) + le compte de tests ; le bloc racine, le seul agrégat « e-CF 39 tests ». RIEN ne confrontait à l'artefact byte-gaté QUEL évènement du pipeline émet un e-CF, sur quel `base_field`, par quel rôle Compta, ni la FormaPago défaut.
**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 « identité
d'un ENSEMBLE » que la colonne « Entité » de la SPEC §4, l'énumération des onglets
Mobile ou les transitions à séparation des pouvoirs du workflow — appliquée à la
**surface data-derived la plus critique du module fiscal** : les `emission_events`
d'`ecf_plan.json`, jamais gatés hors le format e-NCF et le compte de tests.
**Dérive silencieuse fermée** :
- `05_deliverables_mvp/fiscal/ecf_dgii/README.md:40-59` (§Cross-cohérence, « le cœur
du livrable ») — transcrit À LA MAIN : (L48) l'ENSEMBLE des évènements émetteurs
(« seulement réservation et contrat ») + l'ENSEMBLE exclu des brouillons (« on ne
facture pas un brouillon (lead/visite/devis) ») ; (L50) l'ENSEMBLE des `base_field`
(`montant_reservation`/`montant_contrat`) ; (L53-54) le rôle UNIQUE
(`compta-fiscaliste-ecf` · « OTO Compta Fiscaliste eCF » · portail compta) ;
(L59) la FormaPago défaut (« = 3 (Tarjeta) » · Cardnet #10).
- Source faisant autorité (byte-gatée par `check_artifacts`) :
`out/ecf_plan.json` — `emission_events[]` (`update_value`/`base_field`/`role_id`/
`erpnext_role_name`), `forma_pago_defaut` + catalogue `formas_pago`. Double
ancrage du rôle sur `rbac/rbac_50_roles.json` (id → `portail` compta → nom ERPNext).
- Piège #6/#5, la dérive la plus dommageable ici : **AJOUTER un `emission_event` sur
un état BROUILLON** (ex. `devis`) — l'anti-pattern EXACT que le module interdit
(« on ne facture pas un brouillon ») — pendant que L48 dit encore « seulement
réservation et contrat » → l'agent ERPNext Backend câblerait une **facture émise
avant soumission/encaissement**. Aucune suite `tests/` (FONCTIONS de résolution/
composition, jamais la prose vs l'artefact) ne l'attrape.
- **État courant** : émetteurs `{contrat, reservation}`, base_field
`{montant_contrat, montant_reservation}`, rôle unique `compta-fiscaliste-ecf`
(portail compta · RBAC concordant), FormaPago défaut `3`/Tarjeta/Cardnet —
tout recoupe l'artefact EXACTEMENT (anti-invention #6, rien à réécrire). Le
défaut est la **surface ungated**.
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « Fiscal · e-CF DGII —
IDENTITÉ des ÉVÈNEMENTS D'ÉMISSION » avant `sys.exit`) : (0) **cohérence interne** —
rôle UNIQUE sur tous les évènements · défaut ∈ catalogue FormaPago ; (1) **ENSEMBLE
émetteurs** — set-diff README `seulement …` == `update_value` de l'artefact (ni
fantôme ni manquant · accents/casse normalisés) ; (2) **DISJONCTION** — l'ensemble
exclu `(lead/visite/devis)` ∩ émetteurs = ∅ (jamais de facture sur brouillon — mord
un `devis` émetteur ET la self-contradiction du README) ; (3) **ENSEMBLE `base_field`**
— set-diff README == artefact ; (4) **rôle unique** cité backtické + nom ERPNext,
**double-ancré** sur `rbac_50_roles` (id → portail compta → nom concordant) ; (5)
**FormaPago défaut** — code == artefact · label ∈ catalogue · motif cite Cardnet (#10).
Un claim absent échoue AUSSI (traçabilité #6).
**6 morsures vérifiées** : artefact facture un BROUILLON (`devis` émetteur → mismatch
d'ensemble **ET** clash de disjonction · 2 morsures en une) · artefact renomme un
`base_field` (`montant_reservation→montant_acompte`) · artefact change FormaPago défaut
`3→1` (Efectivo) · artefact casse le rôle unique (2ᵉ évènement → `compta-comptable`) ·
README périmé « seulement contrat » (réservation retiré) · motif du défaut ne cite
plus « Cardnet » (#10) ; restauré = green : émetteurs · disjonction · base_field ·
rôle == RBAC · FormaPago == artefact · exit 0. Working tree byte-restauré
(`git checkout --`, **JAMAIS** `git clean`, interdit absolu) · **7 gates re-verts**
(`check_readme_claims`/`check_docs`/`guard_constraints`/`check_artifacts`/
`check_ci_integrity`/`validate_json`/`check_regression`).
- `ci/README.md` (clause récap du pipeline `check-readme-claims` + paragraphe détaillé
« IDENTITÉ des évènements d'émission e-CF ancrée sur `ecf_plan.json` ») 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 connexion
Compupar réelle + l'émission e-CF en production restent côté ERPNext Backend/VPS).
- **Auto-score 4Big** : 96/100.
## Session `20260801_143341` · Buffer S8 · Domaine Faisabilité/generator : les **TROIS copies aval** des 6 MARQUEURS CANONIQUES (`3 %`/`8.5 %`/`52 %` · `USD+DOP`/`Letter US`/`Cardnet`, contraintes NON-NÉGOCIABLES **CLAUDE.md #9/#10**) — l'**oracle d'enforcement** `genlib/renderer.py::_CANONICAL_MARKERS` (la liste que `check_canonical()` PARCOURT pour PROUVER que chaque rendu de faisabilité porte les 6 marqueurs), les **DEUX docstrings** de `genlib/scorer.py` (barème axe 4 + `_score_parametres_canoniques`) et l'**oracle du test** `tests/test_generator.py` (`for marker in [...]`) — étaient **HORS de tout ancrage**. Le bloc « paramètres canoniques » existant n'ancre QUE `model.py::CANONICAL` + le README à #9/#10 ; son propre commentaire NOMMAIT pourtant le défaut (« ces marqueurs sont HARDCODÉS DANS LE TEST, une copie de plus, JAMAIS comparée à CLAUDE.md ») sans le fermer.
**Tâche** : **Sprint 8 · buffer** (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive d'ANCRAGE à CLAUDE.md (classe
[[claude-md-constant-anchor-gate]]). **Même patron EXACT** que le tuple d'oracle du
test `publiciste/branding` ancré sur #4 (une copie hardcodée dans un test, gatée) —
appliqué aux **trois copies aval encore ungated du MÊME module** `faisabilite/generator`
dont le bloc canonique n'ancrait que 2 des 5 copies.
**Dérive silencieuse fermée** :
- `05_deliverables_mvp/faisabilite/generator/genlib/renderer.py:275` —
`_CANONICAL_MARKERS = ["3 %", "8.5 %", "52 %", "USD + DOP", "Cardnet", "Letter US"]`,
la LISTE que `check_canonical()` parcourt pour vérifier que le volet ingénierie
du rendu porte les 6 marqueurs. C'est l'**enforcement même** de l'« axe 4
honnêteté ».
- `05_deliverables_mvp/faisabilite/generator/genlib/scorer.py:7` (barème axe 4) **et**
`:109` (`_score_parametres_canoniques`) — deux docstrings énumérant
« 3 % / 8.5 % / 52 % · USD+DOP · Letter US · Cardnet ».
- `05_deliverables_mvp/faisabilite/generator/tests/test_generator.py:103` —
`for marker in ["3 %", "8.5 %", "52 %", "USD + DOP", "Cardnet", "Letter US"]` :
l'oracle du test lui-même, celui que le commentaire du bloc amont dénonçait.
- Source faisant autorité : **CLAUDE.md #9/#10** (les 6 valeurs), déjà recomputées
en `exp` par le bloc « paramètres canoniques » — RÉUTILISÉES ici (zéro duplication).
- Piège #6 : l'oracle `_CANONICAL_MARKERS` est le juge de l'honnêteté du rendu. S'il
DÉRIVE de `CANONICAL` (donc de #9/#10), le contrôle devient FAUX de deux façons :
soit il exige un marqueur périmé (`Cardnet` alors que le rendu, issu d'un CANONICAL
mis à jour, porterait `Azul` → **faux rouge**), soit on retire un marqueur et le
contrôle **NE VÉRIFIE PLUS RIEN** (vert vacux). La seule suite `tests/` teste des
FONCTIONS (dont `check_canonical` — AVEC son propre oracle hardcodé), jamais
l'ancrage à CLAUDE.md.
- **État courant** : **aucun marqueur périmé** — `set(renderer._CANONICAL_MARKERS) ==
set(model.CANONICAL.values())` vérifié programmatiquement AVANT toute édition ; les
3 copies recoupent #9/#10 exactement (anti-invention #6, rien à réécrire). Le défaut
est la **surface ungated**.
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « Faisabilité/generator
(2e surface) » après le bloc « paramètres canoniques ») : RÉUTILISE `exp` +
`_want6` (6 valeurs normalisées de #9/#10, zéro duplication). Helper `_list_vs_want6`
pour les copies-LISTES : (c) `renderer.py::_CANONICAL_MARKERS` et (e) l'oracle du test
sont parsés (`ast.literal_eval`) puis comparés à l'ensemble EXACT (set-diff : absent
ET en trop · anti-doublon). Pour les copies-DOCSTRINGS (d), chaque valeur normalisée
(`_pnorm`/`_ai` — espaces/accents-insensibles ⇒ `USD+DOP`==`USD + DOP`) doit être
substring de la ligne normalisée ; ≥2 énumérations exigées (barème + fonction). Un
claim/ligne absent échoue AUSSI (traçabilité #6).
**4 morsures vérifiées** : (M1) `renderer._CANONICAL_MARKERS` `Cardnet→Azul`
(absents=[cardnet] · en trop=[azul]) · (M2) oracle du test retire `8.5 %`
(absents=[8.5%]) · (M3) scorer docstring L7 `52 %→55 %` (n'énumère plus [52%]) ·
(M4) **CLAUDE.md #10 `Cardnet→Azul` → mord SIMULTANÉMENT model.py + renderer + test +
les DEUX docstrings** (5 morsures en cascade — la valeur MÊME de l'ancrage à la
source constitutionnelle) ; restauré = green : renderer/test == #9/#10 (6 marqueurs) ·
2 docstrings == #9/#10 · exit 0. Working tree byte-restauré (`git checkout --`,
**JAMAIS** `git clean`, interdit absolu) · **7 gates re-verts**
(`check_readme_claims`/`check_artifacts`/`check_ci_integrity`/`check_docs`/
`guard_constraints`/`check_regression`/`validate_json`).
- `ci/README.md` (clause récap du pipeline `check-readme-claims` + paragraphe détaillé
« 2ᵉ surface du MÊME module : les 3 copies aval des marqueurs canoniques ») 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 génération
réelle des faisabilités reste côté BIM/Faisabilité).
- **Auto-score 4Big** : 96/100.
## Session `20260801_150344` · Buffer S8 · Domaine DevOps CI/CD : gate d'**IDENTITÉ des IDENTIFIANTS D'INFRASTRUCTURE VPS** (IP `153.75.250.214` + les DEUX conteneurs Docker `erpnext-backend-1`/`erpnext-frontend-1`) ancré sur **CLAUDE.md §VPS** — surface data-derived jusqu'ici **HORS de tout ancrage**.
**Tâche** : **Sprint 8 · buffer** (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive d'ANCRAGE à CLAUDE.md (classe
[[claude-md-constant-anchor-gate]] + [[agent-fiche-role-attrs-ungated]], surface
**infra ~ endpoint RunPod**). **Même patron EXACT** que le gate « endpoint GPU
RunPod » (session 130325 : un identifiant d'infra hand-transcrit dans N docs,
recomputé de la source in-repo faisant autorité) — appliqué ici aux identifiants
**serveur** dont la source de vérité est **constitutionnelle** (CLAUDE.md §VPS).
**Dérive silencieuse fermée** : CLAUDE.md §VPS FIXE l'IP du VPS `153.75.250.214`
et les 2 conteneurs Docker `erpnext-backend-1`/`erpnext-frontend-1`. Ces
identifiants sont RECOPIÉS À LA MAIN dans ~10 endroits, sans AUCUN ancrage :
- `README.md` (×2 · VPS hors périmètre + dépôt Gitea) · `02_master_prompt/
MASTER_PROMPT_DTP_v2.md` (IP ×2 + les 2 conteneurs) · `AGENTS_EXISTING_ASSETS.md`
(IP + 2 conteneurs) · `03_agents/mobile/AGENT.md` (`erpnext-backend-1`, backend
cible de l'API) · `05_deliverables_mvp/rbac/RBAC_50_ROLES_SPEC.md` (run-book
d'import) · `05_deliverables_mvp/GAP_ANALYSIS_SPRINT1.md` · `tests/README.md`
(base URL Playwright) · `ci/README.md` (instance Gitea).
- Aucun gate ne couvrait ce lien : `check_docs` ne voit qu'un lien Markdown,
`guard_constraints` qu'un terme interdit, `check_artifacts` qu'un `out/` — une IP
ni un nom de conteneur n'est aucun des trois. Aucune suite `tests/` (FONCTIONS
des modules, jamais la prose infra) ne l'attrape.
- Drift la plus réaliste, #6 : une **MIGRATION serveur** (nouvelle IP) ou un
**RENOMMAGE du projet compose** (`erpnext-*` → `oto-*`) — évènement ops COURANT :
CLAUDE.md §VPS se met à jour, les ~10 copies se PÉRIMENT en silence → un agent
câblerait l'API mobile / les tests e2e / le hand-off d'import vers un serveur
MORT ou un conteneur FANTÔME (l'invention même que #6 interdit).
- **État courant** : aucune copie périmée — l'unique IPv4 du corpus == canonique,
les seuls conteneurs cités ⊆ l'ensemble canonique (vérifié programmatiquement
AVANT édition). Le défaut est la **surface ungated**.
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « CLAUDE.md §VPS · IDENTITÉ
des IDENTIFIANTS D'INFRASTRUCTURE » avant `sys.exit`) : RE-DÉRIVE l'IP + l'ENSEMBLE
des conteneurs de CLAUDE.md §VPS (`^## VPS` → `IP <v4>` + `Docker (…)`, jamais une
liste à la main · #6 ; garde « section/format introuvable » = ancre morte).
Direction copie→source sur `git ls-files '*.md'` (hors `CLAUDE.md` = source, hors
`05_activity_log/` + `daily_reports/` = journaux figés à ne pas réécrire) : tout
token **IPv4** DOIT == l'IP canonique ; tout token **conteneur** DOIT ∈ l'ensemble
canonique. Garde anti-évaporation (IP ET ≥1 conteneur restent cités quelque part).
Le write-path interdit `/var/www/html/static` reste du ressort de
`guard_constraints`, non de ce gate d'identité.
**Piège de conception CORRIGÉ en vérif adversariale** : 1ʳᵉ version ancrait
l'enveloppe conteneur sur le préfixe LITTÉRAL `erpnext-[a-z]+-1` → un renommage de
projet compose dans une copie (`erpnext-backend-1` → `oto-backend-1`, le drift MÊME
qu'on vise) ne matchait plus le shape et **PASSAIT en silence** (M2 vert à tort).
Correctif : l'enveloppe est DÉRIVÉE des **stems de service** canoniques
(`backend`/`frontend`) via `[a-z0-9]+-(?:backend|frontend)-1`, PAS du préfixe →
`oto-backend-1` est vu comme fantôme. Le filtre source est lui aussi générique
(`[a-z0-9]+-[a-z]+-1`) pour que M4 (renommage DANS §VPS) cascade avec un message
utile plutôt qu'un « format modifié » crude.
**5 morsures vérifiées** (mutations sur fichiers DONNÉES uniquement, restaurées par
`git checkout -- <path>` ciblé — **JAMAIS** `git clean`/`checkout` large avec le
gate décommité en cours, interdit absolu + incident process en mémoire) : (M1) IP
périmée dans `README.md` (serveur migré, copie non mise à jour) → stale · (M2)
conteneur `erpnext-backend-1→oto-backend-1` dans le master prompt (compose renommé)
→ fantôme (après correctif) · (M3) IP changée DANS CLAUDE.md §VPS → **cascade sur
les 5 docs citant l'IP** · (M4) conteneurs renommés DANS §VPS → **cascade fantôme
sur les 4 docs citant un conteneur** avec message utile · (M5) header `## VPS`
retiré → ancre morte. Restauré = green : 10 assertions VPS infra (5 IP + 5
conteneur) · exit 0. **7 gates re-verts** (`check_readme_claims`/`check_artifacts`/
`check_ci_integrity`/`check_docs`/`guard_constraints`/`check_regression`/
`validate_json`).
- `ci/README.md` (clause récap du pipeline `check-readme-claims` : sous-clause
« identité d'infrastructure VPS ») mise à jour — et désormais **elle-même couverte
par le gate** (elle gagne les tokens conteneur/IP canoniques, tous verts).
- **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 migration
serveur réelle + le renommage compose restent côté DevOps/VPS production).
- **Auto-score 4Big** : 96/100.
## Session `20260801_153345` · Buffer S8 · Domaine CRM/Commissions : l'**IDENTITÉ des RÔLES PAYÉS** du barème vendeurs — le « **à quel rôle** » de la question fondatrice (« quel évènement paie, à quel RÔLE, sur quel montant », README:8-9) — était HORS de tout gate. La §Cross-cohérence promet (README:51-53) que chaque `role_id` commissionné « doit être résolu depuis `rbac_50_roles.json` … **ET appartenir au portail `ventes`** » (invariant CLI #5) avec un `erpnext_role_name` « cohérent avec RBAC » (invariant #6 · le « nom Frappe résolu » du README:36). C'est une **SÉPARATION DES POUVOIRS** : on ne commissionne QUE des rôles du portail ventes. Les DEUX blocs commissions existants (cross-cohérence états/base_field + anti-invention taux) NE couvraient PAS cette appartenance-portail ni la résolution du nom.
**Tâche** : **Sprint 8 · buffer** (ERPNext Backend / CRM · #6). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive. **Troisième surface data-derived** du même
livrable `crm/commissions` (après états/base_field et anti-taux) — le troisième tiers
de la question fondatrice du module (« à quel rôle »), le seul jamais confronté à
l'artefact. Classe [[agent-fiche-role-attrs-ungated]] : une propriété-rôle
data-derived affirmée en prose mais gatée par rien.
**Dérive silencieuse fermée** :
- `05_deliverables_mvp/crm/commissions/README.md:51-53` — « `role_id` … résolu depuis
`rbac_50_roles.json` … ET appartenir au portail `ventes` » + README:36 « nom Frappe
résolu ».
- Sources faisant autorité (toutes byte-gatées par `check_artifacts`) :
`out/commission_plan.json[evenements[].{role_id,erpnext_role_name}]` (les rôles
RÉELLEMENT payés) · `rbac/rbac_50_roles.json[roles[].{id,portail,erpnext_role_name}]`
+ `[portails_business]` (le contrat) · `out/MANIFEST.json[roles_rbac_utilises,counts.roles]`
(le récap).
- Piège #6, les dérives les plus graves ici : **(a)** commissionner un rôle d'un
AUTRE portail (payer un fiscaliste `compta` → la prose « portail `ventes` » devient
un MENSONGE, pouvoirs mélangés) ; **(b)** un `role_id` FANTÔME absent du contrat
(rôle inventé — l'invention même que #6 interdit) ; **(c)** un `erpnext_role_name`
figé en dur dans le plan qui dérive quand le contrat renomme le rôle (nom Frappe
périmé → `Has Role` importé sur un rôle INEXISTANT côté VPS). Aucune suite `tests/`
(FONCTIONS de résolution/calcul, jamais la prose vs l'artefact) ne l'attrape ; ce
n'est ni un lien (`check_docs`), ni un terme interdit (`guard_constraints`), ni un
`out/` dérivé (`check_artifacts`) → invisible aux trois.
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « CRM/Commissions ·
IDENTITÉ des RÔLES PAYÉS » après le bloc anti-invention) : RE-DÉRIVE tout des
artefacts byte-gatés (zéro dup du contrat du générateur · #6). **(a)** chaque
`role_id` du plan ∈ contrat (sinon fantôme) ; **(b)** `erpnext_role_name` du plan ==
contrat pour chaque évènement (nom Frappe résolu · invariant #6) ; **(c)** le portail
des rôles payés est un **SINGLETON** ∈ `portails_business`, ET == le portail nommé au
README (`appartenir au portail \`([a-z-]+)\`` — le littéral `ventes`) : payer un rôle
d'un autre portail casse le singleton, renommer le portail au contrat désaccorde le
littéral README ; **(d)** `MANIFEST.roles_rbac_utilises` == l'ensemble distinct
`(role_id, nom)` réellement payé + `counts.roles` recoupé.
**6 morsures vérifiées** (mutations sur fichiers DONNÉES uniquement — plan/contrat/
MANIFEST/README — restaurées par `git checkout -- <path>` **ciblé** ; **JAMAIS**
`git checkout .`/large avec le gate décommité en cours d'édition, interdit absolu +
incident process en mémoire) : (M1) `role_id` fantôme dans le plan → absent du contrat ·
(M2) `erpnext_role_name` dérivé dans le plan → ≠ contrat · (M3) rôle **`compta`**
commissionné → portails `{compta, ventes}`, séparation des pouvoirs rompue (+ cascade
MANIFEST) · (M4) littéral portail README `ventes→sales` → dit `sales` MAIS rôles du
portail `ventes` · (M5) `roles_rbac_utilises` amputé → ≠ rôles payés · (M6) renommage
du rôle DANS le contrat (`ventes-conseiller`) → cascade double (nom plan≠contrat +
MANIFEST périmé). Restauré = vert : 4 assertions vertes · exit 0. **7 gates re-verts**
(`check_readme_claims`/`check_artifacts`/`check_ci_integrity`/`check_docs`/
`guard_constraints`/`check_regression`/`validate_json`).
- `ci/README.md` (ligne récap du pipeline `check-readme-claims` : sous-clause « rôles
payés » ; + paragraphe prose « troisième surface du même livrable `crm/commissions` »)
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 confirmation
des taux réels + le câblage du calcul restent côté ERPNext Backend/VPS production).
- **Auto-score 4Big** : 96/100.
## Session `20260801_160354` · Buffer S8 · Domaine QA/comptes de tests · Fiches agents : CINQ **comptes de tests PAR SUITE** écrits HORS de la colonne « Tests » des tables de fiches — `seo` « **36 tests** » (`03_agents/seo/AGENT.md:31`, prose libre) · `frontend_console` « **19 tests** » (portails) & « **31 tests** » (chat_otoia) (`:32`/`:34`, prose libre) · `onapi_legal` « **44 tests** » (`:32`, prose libre) · `devops` « 29 (dont 14 injections négatives) » (`:25`, **cellule de table à suffixe**) — étaient **HORS de tout gate**. Les DEUX blocs de comptes de tests existants ne les atteignent PAS : le bloc **cellules « Tests »** (`row_re`) n'accepte qu'une ancre de lien `` `<mot>/` `` ET une cellule ENTIER NU ; le bloc **agrégats en prose** ne couvre que `crm` (Total CRM) et `erpnext_backend` (RBAC + e-CF).
**Tâche** : **Sprint 8 · buffer** (DevOps CI/CD · QA — « Regression tests
exhaustifs »). Roadmap fonctionnellement close ; poursuite de la série anti-dérive
(CLAUDE.md #6). **Même classe EXACTE** que le bloc « comptes de tests PAR SUITE »
(cellules de table) et le bloc « agrégats EN PROSE » (Total CRM/RBAC/e-CF) — appliquée
à la **dernière poche** de la même surface : les claims per-suite que ces deux blocs
ne balaient pas par construction de leurs regex.
**Dérive silencieuse fermée** :
- `03_agents/seo/AGENT.md:31` — « **36 tests**, gated dans le pipeline » (suite `seo`).
- `03_agents/frontend_console/AGENT.md:32` — « **19 tests** · job CI
`frontend-portails-tests` » (suite `frontend/portails`) · `:34` — « **31 tests** ·
job CI `chat-otoia-tests` » (suite `frontend/chat_otoia`).
- `03_agents/onapi_legal/AGENT.md:32` — « **44 tests** (dont 8 négatifs) · job CI
`legal-confotur-tests` » (suite `legal/confotur`).
- `03_agents/devops/AGENT.md:25` — cellule « Tests » du run-book :
« 29 (dont 14 injections négatives) » (suite `devops/deploy_runbook`). Cette ligne
échappe à `row_re` **DEUX fois** : (a) l'ancre de lien est un CHEMIN complet
(`05_deliverables_mvp/devops/deploy_runbook/`, pas `` `<mot>/` ``) et (b) la cellule
n'est PAS un entier nu (suffixe « (dont …) »).
- Source faisant autorité : `qa/regression/out/regression_plan.json`
(`suites[].test_methods` · byte-gaté par `check_artifacts`), déjà chargée dans le
dict `auth` que les deux blocs amont utilisent (zéro duplication #6). Les 5 recomputes :
`seo`=36 · `frontend/portails`=19 · `frontend/chat_otoia`=31 · `legal/confotur`=44 ·
`devops/deploy_runbook`=29.
- Piège : ajouter/retirer un `def test_` à l'une de ces 5 suites fait DÉRIVER en
silence le nombre de la fiche pendant que la matrice de régression dit autre chose.
Une compensation entre deux suites (l'une +1, l'autre 1) laisserait même la SOMME
agrégée (« 564 tests · 22 suites », elle gatée) juste ET ces lignes fausses —
exactement le motif per-suite que le bloc des cellules corrige, sur des surfaces
qu'il ne balaie pas. Aucune suite `tests/` (qui teste des FONCTIONS, pas la prose
des fiches) n'attrape ce « vert trompeur ».
- **État courant** : **aucun compte périmé** — les 5 recoupent
`plan.suites[].test_methods` exactement (anti-invention #6, rien à réécrire). Le
défaut est la **surface ungated**.
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « Fiches agents · comptes
de tests PAR SUITE écrits HORS de la colonne Tests » après le bloc des agrégats en
prose e-CF) : table de config `PER_SUITE_HORS_TABLE` = (fiche · suite_path · motif ·
label). Pour chaque claim : (1) on RECOMPUTE `exp = auth[suite_path]` (même source
byte-gatée déjà chargée · zéro duplication) ; (2) motif ANCRÉ sur le nom de job CI
adjacent (ou le lien du module pour devops) pour viser LA bonne suite SANS ambiguïté
(les deux claims `frontend_console` 19/31 sont désambiguïsés par leurs job CI
distincts) ; (3) claim absent ⇒ ROUGE (traçabilité #6). Garde `suite_path ∈ auth`
(anti-câblage-rompu).
**7 morsures vérifiées** : (M1) fiche seo `36→35` · (M2) fiche portails `19→20` ·
(M3) fiche onapi `44→43` · (M4) **cellule devops** `29→30` (prouve que le suffixe
« (dont …) » + ancre chemin-complet est bien couvert) · (M5) **SOURCE drift** —
`plan.suites[seo].test_methods 36→37` ⇒ fiche périmée (prouve que le gate suit
l'ARTEFACT, pas un oracle figé) · (M6) ligne prose seo retirée ⇒ INTROUVABLE
(traçabilité) · (M7) fiche chat_otoia `31→32` ⇒ mord SEULE la ligne chat_otoia sans
toucher portails (prouve la désambiguïsation par job CI). Restauré = green : 5 claims
== `plan.suites[].test_methods` · exit 0. Working tree byte-restauré
(`git checkout --`, **JAMAIS** `git clean`, interdit absolu) · **7 gates re-verts**
(`check_readme_claims`/`check_docs`/`guard_constraints`/`validate_json`/
`check_artifacts`/`check_ci_integrity`/`check_regression`).
- `ci/README.md` (clause du récap `check-readme-claims` : « comptes de tests par
suite écrits HORS de cette colonne ») mis à jour.
- **Observé, hors périmètre de CE gate** : `03_agents/devops/AGENT.md:24` cellule
« 22 suites gated » = COMPTE de suites (`plan.totals.suites`), classe DISTINCTE du
`test_methods` per-suite ; déjà ancré via l'agrégat QA « 22 suites gated · 564
tests » — candidat propre pour une session future si confirmé ungated.
- **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 ; les suites
réelles tournent côté runner Gitea).
- **Auto-score 4Big** : 96/100.
@@ -120,7 +120,7 @@
"criterion": "DOC", "criterion": "DOC",
"weight": 20, "weight": 20,
"passed": true, "passed": true,
"evidence": "RBAC_50_ROLES_SPEC.md (10040 octets)" "evidence": "RBAC_50_ROLES_SPEC.md (10039 octets)"
}, },
{ {
"criterion": "CONTRAT", "criterion": "CONTRAT",
@@ -44,12 +44,12 @@ technique transversale** (`plateforme`, hors du compte de 5) :
| Portail | Familles rattachées | Nb rôles | | Portail | Familles rattachées | Nb rôles |
|---|---|---| |---|---|---|
| **direction** | direction, finance (CFO), faisabilite (analyste), legal | 10 | | **direction** | direction, finance (CFO), faisabilite (analyste), legal | 9 |
| **ventes** | ventes, marketing | 12 | | **ventes** | ventes, marketing | 12 |
| **construction** | construction, faisabilite (rendu/IFC/économiste) | 10 | | **construction** | construction, faisabilite (rendu/IFC/économiste) | 10 |
| **achat** | achat | 5 | | **achat** | achat | 5 |
| **compta** | finance | 8 | | **compta** | finance | 8 |
| **plateforme** _(technique)_ | plateforme | 5 | | **plateforme** _(technique)_ | plateforme | 6 |
| | **Total** | **50** | | | **Total** | **50** |
> Répartition indicative issue de `rbac_50_roles.json` ; la source de vérité reste > Répartition indicative issue de `rbac_50_roles.json` ; la source de vérité reste
+229 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff