e53d60fc0b
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
682 lines
54 KiB
Markdown
682 lines
54 KiB
Markdown
# Activity Log · 2026-08-01 · Claude Code DTP
|
||
|
||
## Session `20260801_000201` · Buffer S8 · Domaine RBAC/userperm_gen : la **table « Mapping `scope_donnees` → mécanisme »** — la **FONCTION d'enforcement ROW-LEVEL** (quel mécanisme Frappe natif applique CHAQUE portée de données + si un `User Permission` template est émis), CŒUR sécurité du module — était transcrite EN PROSE (README:33-38) SANS AUCUN gate d'IDENTITÉ. Le bloc RBAC « 3 volets » existant ne gate QUE la **ventilation** par mécanisme (« 28 `entite` · 16 `groupe` · 2 `own` · 4 `equipe` »), un COMPTE aveugle à QUEL mécanisme applique QUELLE portée.
|
||
|
||
**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 patron d'IDENTITÉ que
|
||
le mapping RBAC `fixtures_gen` (séparation des pouvoirs `set_user_permissions`) ou la
|
||
cross-cohérence PERMISSIONS Legal/CONFOTUR (`role_id`→actions) — appliqué à la
|
||
**surface data-derived distincte du même README `rbac/userperm_gen`** : la table
|
||
qui associe chaque portée de données à son mécanisme d'enforcement, jamais gatée hors
|
||
son compte agrégé.
|
||
|
||
**Dérive silencieuse fermée** :
|
||
- `05_deliverables_mvp/rbac/userperm_gen/README.md:33-38` — table « Mapping
|
||
`scope_donnees` → mécanisme » : nomme, PAR portée, le `mechanism` Frappe natif
|
||
(`own`→`docperm_if_owner` · `entite`→`user_permission_company` ·
|
||
`groupe`→`none_consolidated` · `equipe`→`vps_confirm_team`) ET le verdict
|
||
« Template émis ? » (`oui` pour `entite` seul, sinon `non (null)`).
|
||
- Source faisant autorité (byte-gatée par `check_artifacts`) :
|
||
`out/user_permission_plan.json` — chaque entrée porte `scope_donnees` +
|
||
`mechanism` + `user_permission_template` (null ou objet). Le mapping est une
|
||
**fonction** : chaque portée → exactement un mécanisme ; template émis SSI
|
||
`entite`/`user_permission_company`.
|
||
- Piège : le bloc de ventilation est AVEUGLE à l'identité de ces couples.
|
||
RÉAFFECTER une portée à un mécanisme plus permissif (`entite` →
|
||
`none_consolidated` : le **row-level enforcement ABANDONNÉ**, sur-exposition des
|
||
données inter-entités — la restriction même que le module pose), RENOMMER un
|
||
mécanisme ou BASCULER le verdict « Template émis ? » laisse le README périmé
|
||
pendant que l'artefact dit autre chose → l'agent ERPNext Backend câblerait le
|
||
mauvais mécanisme (le risque même que la table veut prévenir). Aucune suite
|
||
`tests/` (qui teste des FONCTIONS de mapping/résolution, pas la prose) n'attrape
|
||
ce « vert trompeur ».
|
||
- **État courant** : **aucune ligne périmée** — les 4 couples portée→mécanisme et
|
||
leurs verdicts recoupent 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 « RBAC/userperm_gen
|
||
Mapping » après le bloc CRM gardes) : (1) **par portée** — le `mechanism` (1er token
|
||
backtické de la colonne) ET le verdict « Template émis ? » (`**oui**` / `non (null)`)
|
||
recomputés de `user_permission_plan.json`, prose exigée EXACTE ; (2) **identité
|
||
d'ensemble** — portées de la table == portées de l'artefact (set-diff : ni fantôme
|
||
ni manquante). Cohérences croisées en bonus (mordent un plan INTERNEMENT incohérent) :
|
||
chaque portée mappe UN SEUL mécanisme (fonction, pas relation) · « Template émis »
|
||
UNIFORME sur les entrées d'une portée · template émis EXACTEMENT pour
|
||
`user_permission_company` (l'invariant « template SSI `entite` » du README:36/61) ·
|
||
ensemble des portées NON VIDE. Un claim absent échoue AUSSI (traçabilité).
|
||
|
||
**7 morsures vérifiées** : README réaffecte `entite`→`none_consolidated`
|
||
(mécanisme périmé · élévation de portée) · README bascule `entite` « Template
|
||
émis » oui→non (verdict périmé) · README échange une portée `groupe`→`famille`
|
||
(absents=[groupe] + en trop=[famille] + ligne INTROUVABLE) · README retire la
|
||
ligne `own` (absents=[own]) · table entière supprimée (les 4 absents + toutes
|
||
lignes INTROUVABLES) · **artefact** réaffecte toutes les entrées `entite`→
|
||
`none_consolidated` (README périmé sur mécanisme ET verdict) · **artefact** rend
|
||
une entrée `own` porteuse d'un template (« Template émis » NON uniforme
|
||
[False,True] · invariant SSI cassé) ; restauré = green : 4 portées · table ==
|
||
artefact · par-ligne exact · fonction/uniforme/SSI verts · exit 0. Working tree
|
||
byte-restauré (`git checkout --`, **JAMAIS** `git clean`) · **7 gates re-verts**.
|
||
|
||
- `ci/README.md` (table récap du pipeline + paragraphe détaillé « 2ᵉ surface
|
||
`rbac/userperm_gen` ») 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).
|
||
- **Auto-score 4Big** : 96/100.
|
||
|
||
## Session `20260801_003201` · Buffer S8 · Domaine Faisabilité/bancable : les **4 figures « Génération réelle (fixture) »** du README (`40 unités · valeur catalogue USD 8,560,000 / DOP 505,040,000 · point d'équilibre 21 unités (⌈52 % × 40⌉)`) — les chiffres DATA-DERIVED que le **dossier bancable** (l'artefact que voit le banquier) recopie — étaient **HORS de tout gate**. Le module n'écrit son `out/` que sur disque (« out/ non commité ») ⇒ **check_artifacts est aveugle** (aucun artefact byte-gaté) et **AUCUN** gate ne le touchait.
|
||
|
||
**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 esprit que les
|
||
paramètres canoniques `faisabilite/generator` **ancrés sur CLAUDE.md #9/#10** (le
|
||
`52 %` réutilisé ici) — appliqué à une **surface encore jamais gatée** : un module
|
||
dont la source de vérité n'est PAS un `out/*.json` mais **la fixture commitée + le
|
||
générateur**.
|
||
|
||
**Dérive silencieuse fermée** :
|
||
- `05_deliverables_mvp/faisabilite/bancable/README.md:103-105` — ligne
|
||
« Génération réelle (fixture) » : `40 unités` (= Σ quantités typologies) ·
|
||
`USD 8,560,000` (= Σ qté×prix_usd) · `DOP 505,040,000` (= Σ qté×prix_dop) ·
|
||
`21 unités (⌈52 % × 40⌉)` (= ⌈point_equilibre_pct × Σ unités⌉).
|
||
- Source faisant autorité : `fixtures/brief_bancable.json` (COMMITÉE) + la
|
||
**génération réelle** `python3 bancable_gen.py validate` (émet le manifeste sur
|
||
stdout, `figures_calculees[]`). Le `52 %` est **ancré sur CLAUDE.md #9**
|
||
(« Point équilibre 52% pré-vente »), pas recopié.
|
||
- Piège : la seule vérification existante (`tests/`) teste des **FONCTIONS**
|
||
(l'arithmétique de recoupement `_check_derived_arithmetic`) avec des **oracles
|
||
HARDCODÉS** dans le test — une copie de plus, jamais comparée à la PROSE du
|
||
README. Éditer une typologie de la fixture (quantité/prix), en ajouter/retirer
|
||
une, ou déplacer le point d'équilibre de CLAUDE.md #9, laisse les 4 chiffres du
|
||
README périmés pendant que le générateur produit autre chose → le banquier lit
|
||
un dossier faux (l'invention même que #6 interdit).
|
||
- **État courant** : **aucun chiffre périmé** — les 4 figures recoupent la
|
||
génération réelle exactement (anti-invention #6, rien à réécrire). Le défaut est
|
||
la **surface ungated** (module entièrement hors CI).
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « Faisabilité/bancable
|
||
figures » avant `sys.exit`) : (1) **recompute indépendant** des 4 figures depuis
|
||
la fixture commitée ; (2) **cohérence croisée** — le manifeste GÉNÉRÉ
|
||
(`bancable_gen validate`) == le recompute (mord un générateur/fixture
|
||
incohérents) ; (3) **prose** — un seul pattern d'identité exige les 4 valeurs
|
||
EXACTES, **avec séparateur de milliers** pour USD/DOP, + la formule `⌈pct × Σ
|
||
unités⌉` dont le pct == CLAUDE.md #9 et le total == Σ unités. Un claim absent
|
||
échoue AUSSI (traçabilité). Le `out/` n'étant pas commité, le gate **exécute le
|
||
générateur** (stdlib pur, `subprocess`, `encoding="utf-8"`) au lieu de lire un
|
||
artefact byte-gaté — même patron que le bloc `faisabilite/generator` qui lit
|
||
`model.py`.
|
||
|
||
**7 morsures vérifiées** : README `USD 8,560,000→9,560,000` (figure périmée) ·
|
||
README retire le séparateur de milliers (`8,560,000→8560000`) · README breakeven
|
||
`21→20` (formule périmée) · README formule `52 %→55 %` (dérive CLAUDE.md #9) ·
|
||
README supprime la ligne entière (INTROUVABLE · claim absent) · **fixture** Studio
|
||
`qté 12→13` (5 morsures : units/USD/DOP/breakeven README périmés + manifeste) ·
|
||
**fixture** 2 Chambres `prix_usd 320000→300000` (catalogue USD périmé) ; restauré =
|
||
green : 4 figures == génération réelle · manifeste == recompute · pct 52 % ==
|
||
CLAUDE.md #9 · exit 0. Working tree byte-restauré via `git checkout --` (JAMAIS
|
||
`git clean`, interdit absolu) · **7 gates re-verts**.
|
||
|
||
- `ci/README.md` (ligne récap du pipeline `check-readme-claims`) 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 ; conversion PDF + rebuild
|
||
Portail Bancables restent côté VPS).
|
||
- **Auto-score 4Big** : 96/100.
|
||
|
||
## Session `20260801_010204` · Buffer S8 · Domaine Mobile/fiche agent : la **fiche d'identité du rôle RBAC `plateforme-mobile`** (`03_agents/mobile/AGENT.md:36`) — le **SEUL ancrage in-repo réellement vérifiable** de l'agent mobile (builds/soumissions stores étant hors-repo, #8) — recopiait EN PROSE, DEPUIS le contrat `rbac_50_roles.json`, TOUS les attributs data-derived du rôle (`erpnext_role_name` · `nom_fr` · `famille`/`portail` · `entite_principale` · `niveau` · `scope_donnees` · `modules` · permission `API Access` custom R/W · `description`) SANS AUCUN gate d'IDENTITÉ. Le README `mobile/app_config` n'est gaté que sur des COMPTES agrégés (« 5 onglets · 44 rôles couverts · 3 langues · 13 identifiants »), AVEUGLES à l'identité de CE rôle.
|
||
|
||
**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 patron d'IDENTITÉ que
|
||
la table gardes CRM/`workflow_vente` (rôle qui garde chaque transition) ou le mapping
|
||
RBAC `userperm_gen` (portée→mécanisme) — appliqué à la **surface distincte** de la
|
||
fiche agent mobile : la carte que l'agent ERPNext Backend lit pour **seeder le rôle**
|
||
(`seed_mobile_rbac.py`), jamais gatée hors les comptes agrégés de `app_config`.
|
||
|
||
**Dérive silencieuse fermée** :
|
||
- `03_agents/mobile/AGENT.md:36` — ligne « Points de contact réellement commités »,
|
||
cellule « Ce qu'il fixe » : nomme, PAR attribut, le rôle ERPNext
|
||
**`OTO Plateforme Mobile`** (« Développeur Mobile » · famille/portail
|
||
**`plateforme`** · entité **9060 QC** · niveau 2 · scope `groupe` · modules
|
||
`Core`+`Website` · perm `API Access` custom R/W · description « Maintient
|
||
l'app Expo/React Native et l'API mobile ; gère les builds EAS et les
|
||
soumissions stores »).
|
||
- Source faisant autorité : `05_deliverables_mvp/rbac/rbac_50_roles.json` (contrat
|
||
RBAC, schéma-validé · les 3 MANIFEST RBAC recomputés en dérivent et sont byte-gatés
|
||
par `check_artifacts`) → le rôle d'`id` `plateforme-mobile` porte chacun de ces
|
||
champs. Autres blocs du gate lisent déjà ce contrat directement (userperm,
|
||
workflow_vente, confotur).
|
||
- Piège : la seule vérification existante (`tests/`) teste des **FONCTIONS** de
|
||
résolution RBAC, jamais la PROSE d'une fiche agent. Éditer le contrat — élever le
|
||
`scope_donnees` (`groupe`→`entite` : sur-portée inter-entités, la restriction même
|
||
que le rôle pose), changer le `niveau`/l'`entite_principale`, renommer le rôle,
|
||
ajouter/retirer un module ou élargir la permission (`R/W`→`R/W/D`) — laisse la fiche
|
||
périmée pendant que le contrat dit autre chose → l'agent ERPNext Backend câblerait
|
||
le mauvais rôle (le risque même que la fiche veut prévenir).
|
||
- **État courant** : **aucun attribut périmé** — les 9 attributs + la description
|
||
recoupent le contrat exactement (anti-invention #6, rien à réécrire). Le défaut est
|
||
la **surface ungated**.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « Fiche Mobile » avant
|
||
`sys.exit`) : (1) **par attribut** — `erpnext_role_name` · `nom_fr` · `portail` ·
|
||
`entite_principale` · `niveau` · `scope_donnees` · `modules` · permission recomputés
|
||
du contrat, prose EXACTE exigée (regex par attribut, lookaheads anti-préfixe pour
|
||
niveau/modules/perm) ; (2) **permission** — le sigle des verbes est **canonique et
|
||
ordonné** (C/R/W/D/S/X/A) : élargir OU rétrécir la perm change le sigle attendu (un
|
||
préfixe `R/W` ne satisfait plus `R/W/D`, ni l'inverse) ; verbe inconnu ⇒ ROUGE
|
||
(étendre le gate) ; (3) **description** recoupée **verbatim** (emphase markdown `**` et
|
||
point final neutralisés). Cohérences croisées bonus (mordent un contrat INTERNEMENT
|
||
incohérent) : `famille == portail` (bijection portail) · permission bien `custom` ·
|
||
`role_id` présent EXACTEMENT une fois. Un claim absent échoue AUSSI (traçabilité :
|
||
ligne de rôle INTROUVABLE).
|
||
|
||
**12 morsures vérifiées** (6 côté contrat = fiche périmée · 6 côté fiche = fiche
|
||
fausse) : contrat `scope groupe→entite` (sur-portée) · contrat `niveau 2→3` · contrat
|
||
`+delete` (sigle `R/W→R/W/D`) · contrat description réécrite · contrat rôle renommé
|
||
(`OTO Plateforme Mobile→OTO Mobile Dev`) · fiche `modules Website→Selling` · fiche
|
||
`entité 9060 QC→WAF` · fiche perm `R/W→R/W/D` (sur-revendication) · fiche perm `R/W→R`
|
||
(sous-revendication) · fiche modules over-claim `+Selling` et under-claim `Core` seul ·
|
||
ligne de rôle entièrement supprimée (INTROUVABLE) ; restauré = green : 9 attributs +
|
||
description == contrat · famille==portail · perm custom · role_id singleton · exit 0.
|
||
Working tree byte-restauré (`git checkout --`, **JAMAIS** `git clean`, interdit
|
||
absolu) · **7 gates re-verts**.
|
||
|
||
- `ci/README.md` (table récap du pipeline + paragraphe détaillé « Fiche d'identité
|
||
du rôle RBAC mobile ») 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
|
||
du rôle `bench` reste côté VPS).
|
||
- **Auto-score 4Big** : 96/100.
|
||
|
||
## Session `20260801_013208` · Buffer S8 · Domaine Faisabilité/fiches agents : les **deux FICHES D'IDENTITÉ** des rôles RBAC `faisabilite-rendu-3d` (`03_agents/rendu/AGENT.md:34`) et `faisabilite-ifc-speckle` (`03_agents/ifc_speckle/AGENT.md:34`) — la carte que l'agent ERPNext Backend lit pour **seeder** le rôle porteur des rendus / de l'export IFC→GLB — recopiaient EN PROSE, DEPUIS le contrat `rbac_50_roles.json`, les attributs data-derived du rôle (`erpnext_role_name` · `nom_fr`/`modules` côté rendu · `portail` · `entite_principale` · `niveau` · les **deux** `permissions_cibles` `File` R/W/create + `Faisabilité` R ou R/W) SANS AUCUN gate d'IDENTITÉ. Le README `frontend/portails` n'est gaté que sur des COMPTES de rôles par Workspace, AVEUGLES à l'identité de CE rôle.
|
||
|
||
**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 que la Fiche
|
||
Mobile de la session précédente (`plateforme-mobile`) — surface `03_agents/*/AGENT.md`
|
||
qui transcrit à la main les attributs d'un rôle depuis `rbac_50_roles.json`, gatée (si
|
||
tant est) seulement sur des comptes agrégés — appliquée aux **deux fiches faisabilité
|
||
construction** encore ungated. Mémoire `agent-fiche-role-attrs-ungated` mise à jour.
|
||
|
||
**Dérive silencieuse fermée** :
|
||
- `03_agents/rendu/AGENT.md:34` — cellule « Ce qu'il fixe » du rôle
|
||
`faisabilite-rendu-3d` : `OTO Faisabilité Rendu 3D` · « Spécialiste Rendu 3D » ·
|
||
portail `construction` · `WA SRL` · niveau 2 · module `OTOV7 Faisabilité` ·
|
||
perms `File` R/W/create + `Faisabilité` R.
|
||
- `03_agents/ifc_speckle/AGENT.md:34` — cellule du rôle `faisabilite-ifc-speckle` :
|
||
`OTO Faisabilité IFC Speckle` · portail `construction` · `WA SRL` · niveau 2 ·
|
||
perms `File` R/W/create + `Faisabilité` **R/W** (écriture en plus vs rendu).
|
||
- Source faisant autorité : `05_deliverables_mvp/rbac/rbac_50_roles.json` (contrat
|
||
RBAC schéma-validé · les 3 MANIFEST RBAC en dérivent, byte-gatés par
|
||
`check_artifacts`). Plusieurs blocs du gate lisent déjà ce contrat directement.
|
||
- Piège : la seule vérification existante (`tests/`) teste des **FONCTIONS** de
|
||
résolution RBAC, jamais la PROSE d'une fiche. Élargir `Faisabilité` R→R/W (le rôle
|
||
Rendu 3D gagnerait l'écriture sur un DocType qu'il ne doit que LIRE), changer
|
||
`niveau`/`entite_principale`, renommer le rôle ou permuter un module laisse la fiche
|
||
périmée pendant que le contrat dit autre chose → l'agent câblerait le mauvais rôle.
|
||
- **Constat #6 (honnêteté)** : la **DESCRIPTION** de ces deux fiches est une
|
||
**PARAPHRASE éditoriale** (le contrat porte « (… — CLAUDE.md) » / « les modèles »
|
||
que la fiche condense) — vérifié programmatiquement AVANT toute édition. Elle n'est
|
||
donc **PAS** gatée verbatim (ne rien réécrire de correct pour satisfaire un gate) ;
|
||
seuls les attributs **structurés**, eux transcrits à l'exact, sont contraints.
|
||
- **État courant** : aucun attribut structuré périmé — les 12 recomputes recoupent
|
||
le contrat exactement (anti-invention #6, rien à réécrire). Le défaut est la
|
||
**surface ungated**.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « Fiches Faisabilité »
|
||
avant `sys.exit`) : table de config par fiche (fichier · role_id · liste des attributs
|
||
RESTITUÉS). Pour chaque fiche : (1) **par attribut** — `erpnext_role_name` · `nom_fr` ·
|
||
`portail` · `entite_principale` · `niveau` · `modules` · les deux permissions
|
||
recomputés du contrat, prose EXACTE exigée (regex par attribut, lookaheads
|
||
anti-préfixe niveau/module/perms) ; (2) **permissions** — chaîne
|
||
« \`Doctype\` R/W/create + \`Autre\` R » rendue en **ordre canonique** (read→R ·
|
||
write→W · create/delete/submit/… en toutes lettres) : élargir OU rétrécir change la
|
||
chaîne attendue (`R/W/create` ne satisfait plus `R/W/create/delete`) ; verbe inconnu ⇒
|
||
ROUGE. Un claim absent échoue AUSSI (ligne de contact INTROUVABLE = régression #6).
|
||
|
||
**11 morsures vérifiées** (5 côté contrat = fiche périmée · 6 côté fiche = fiche
|
||
fausse) : contrat rendu `Faisabilité +write` (sigle `R→R/W`) · contrat rendu
|
||
`niveau 2→3` · contrat rendu `WA SRL→WAF` · contrat rendu rôle renommé · contrat ifc
|
||
`Faisabilité R/W→R` · fiche rendu perm `File R/W/create→…/delete` (over-claim) · fiche
|
||
ifc perm `R/W→R/W/create` (over-claim) · fiche rendu module `Faisabilité→Selling` ·
|
||
fiche ifc `WA SRL→WAF` · fiche rendu `nom_fr` retiré (claim absent) · ligne de contact
|
||
ifc supprimée (INTROUVABLE) ; restauré = green : 12 attributs == contrat · exit 0.
|
||
Working tree byte-restauré (`git checkout --`, **JAMAIS** `git clean`, interdit
|
||
absolu) · **7 gates re-verts**.
|
||
|
||
- `ci/README.md` (table récap du pipeline + paragraphe détaillé « deux fiches
|
||
d'identité des rôles RBAC faisabilité ») 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 `bench` reste côté VPS).
|
||
- **Auto-score 4Big** : 96/100.
|
||
|
||
## Session `20260801_020209` · Buffer S8 · Domaine Fiches agents/rattachement Workspace : les **assertions d'appartenance `Has Role`** des trois fiches `03_agents/{rendu,ifc_speckle,mobile}/AGENT.md` — QUELLE CONSOLE (Workspace ERPNext natif) le rôle peut atteindre — étaient une surface data-derived **DISTINCTE du contrat** `rbac_50_roles.json` (la membership vit dans `frontend/portails/out/workspace.json`, byte-gaté) et **HORS de tout gate**. Les blocs Fiches Faisabilité / Fiche Mobile des sessions précédentes ne gataient que les attributs du **contrat** ; le bloc « triplets par workspace » ne gate que le **compte** de rôles par Workspace (`nb_roles`), AVEUGLE à QUEL rôle.
|
||
|
||
**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 famille de fiches que
|
||
les deux sessions précédentes (`agent-fiche-role-attrs-ungated`) mais **surface
|
||
orthogonale** : non plus les attributs recopiés du contrat, mais la **membership
|
||
`Has Role`** — l'accès console — qu'aucun gate ne touchait.
|
||
|
||
**Dérive silencieuse fermée** :
|
||
- `03_agents/rendu/AGENT.md:35` — « rattaché au Workspace `OTO Construction` »
|
||
(assertion NOMMÉE) ; `03_agents/ifc_speckle/AGENT.md:35` — « rattaché à un
|
||
Workspace » (générique) ; `03_agents/mobile/AGENT.md:41` — « **volontairement pas**
|
||
rattaché au Workspace `OTO Ventes` » (assertion NÉGATIVE d'honnêteté #6).
|
||
- Source faisant autorité : `frontend/portails/out/workspace.json` (les 5 Workspaces
|
||
natifs v15, chacun portant sa liste `Has Role`, byte-gaté par `check_artifacts` via
|
||
la reproductibilité `out/`). L'`erpnext_role_name` de chaque rôle est résolu du
|
||
contrat `rbac_50_roles.json` par `id` (zéro duplication).
|
||
- Piège : rattacher le rôle mobile à `OTO Ventes` = **sur-exposition console** (le
|
||
dev mobile gagnerait l'accès au portail Ventes — la restriction même que la note
|
||
#6 pose), retirer/**déplacer** le rôle rendu de `OTO Construction` = perte/erreur
|
||
d'accès console — laisse la fiche périmée EN SILENCE pendant que l'artefact dit
|
||
autre chose ⇒ l'agent ERPNext Backend câblerait le mauvais accès console. Aucune
|
||
suite `tests/` (qui teste des FONCTIONS de résolution, pas la prose) n'attrape ce
|
||
« vert trompeur ».
|
||
- **État courant** : aucune assertion périmée — rendu ∈ {`OTO Construction`} seul ·
|
||
ifc ∈ {`OTO Construction`} (≥1) · mobile ∈ ∅ (donc absent de `OTO Ventes`) ; les 3
|
||
recoupent workspace.json 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 ·
|
||
rattachement Workspace (`Has Role`) » avant `sys.exit`) : index recomputé
|
||
`erpnext_role_name → {titres de Workspace le portant}` depuis workspace.json. Par
|
||
fiche, 3 formes d'assertion : **in_named** (rendu) — la prose cite CE Workspace ET la
|
||
membership == {ce Workspace} seul (membership élargie ou déplacée ⇒ ROUGE) ;
|
||
**in_any** (ifc) — membership ≥ 1 Workspace ; **not_in_named** (mobile) — rôle ABSENT
|
||
du `Has Role` du Workspace nommé. Garde anti-typo : un Workspace nommé absent de
|
||
l'artefact échoue avant tout (anti-cible fantôme). Un claim absent échoue AUSSI
|
||
(traçabilité #6).
|
||
|
||
**9 morsures vérifiées** (4 côté artefact · 5 côté fiche/bord) : artefact ajoute le
|
||
rôle mobile à `OTO Ventes` (**sur-exposition #6 VIOLÉE**) · artefact retire le rôle
|
||
rendu de `OTO Construction` · artefact déplace rendu vers `OTO Achat` · artefact
|
||
retire ifc de tous les Workspaces · fiche rendu renomme le Workspace
|
||
(`OTO Construction→OTO Ventes`, INTROUVABLE) · fiche rendu supprime l'assertion ·
|
||
fiche mobile nomme un Workspace fantôme (`OTO Marketing`, anti-typo) · fiche ifc
|
||
supprime l'assertion générique · fiche mobile passe le négatif au positif (retire
|
||
« volontairement pas ») ; restauré = green : rendu=={`OTO Construction`} · ifc≥1 ·
|
||
mobile∉`OTO Ventes` · exit 0. Working tree byte-restauré (`git checkout --`, **JAMAIS**
|
||
`git clean`, interdit absolu) · **7 gates re-verts**.
|
||
|
||
- `ci/README.md` (table récap du pipeline + paragraphe détaillé « rattachement
|
||
Workspace des fiches agents ») mis à jour. Mémoire `agent-fiche-role-attrs-ungated`
|
||
à actualiser (surface membership couverte).
|
||
- **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'attachement
|
||
réel des rôles aux Workspaces `bench` reste côté VPS).
|
||
- **Auto-score 4Big** : 96/100.
|
||
|
||
## Session `20260801_023211` · Buffer S8 · Domaine Publiciste/branding : les **4 TOKENS DESIGN CANONIQUES** de la marque luxury — fond dark `#0a0a12` + accent doré `#f0b429`, titres **Fraunces** + corps **Cormorant Garamond** (contrainte NON-NÉGOCIABLE **CLAUDE.md #4**) — étaient RECOPIÉS à **QUATRE** endroits **tous NON gatés** : (a) les CONSTANTES `lib/branding.py` (`COLOR_BG`/`COLOR_ACCENT`/`FONT_DISPLAY`/`FONT_BODY`), la source unique importée par le générateur & substituée dans le gabarit (`{{COLOR_BG}}…`), hors `out/` donc **invisible à check_artifacts** ; (b) le **docstring** du même fichier ; (c) le README (ligne du gabarit) ; (d) le **tuple d'oracle HARDCODÉ** de `tests/test_publiciste.py`.
|
||
|
||
**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 patron EXACT** que
|
||
les paramètres canoniques `faisabilite/generator` **ancrés sur CLAUDE.md #9/#10**
|
||
(`CANONICAL` ↔ contrainte) — appliqué à la **contrainte #4** (identité visuelle) et
|
||
à son consommateur `publiciste/branding`, module **entièrement hors CI** jusqu'ici
|
||
(grep `branding|0a0a12|f0b429|Fraunces` dans `ci/` = 0 avant cette session).
|
||
|
||
**Dérive silencieuse fermée** :
|
||
- `05_deliverables_mvp/publiciste/lib/branding.py:13-20` — les 4 CONSTANTES ·
|
||
`05_deliverables_mvp/publiciste/lib/branding.py:3-4` — le docstring qui les cite ·
|
||
`05_deliverables_mvp/publiciste/README.md:43` — « Gabarit dark+doré (Fraunces +
|
||
Cormorant Garamond) » · `05_deliverables_mvp/publiciste/tests/test_publiciste.py:193`
|
||
— `for token in ("#0a0a12", "#f0b429", "Fraunces", "Cormorant Garamond")`.
|
||
- Source faisant autorité : **CLAUDE.md #4** (ligne 18) — la contrainte
|
||
constitutionnelle, comme #9/#10 le sont pour le générateur de faisabilité.
|
||
- Piège : le SEUL contrôle existant (`test_marque_luxury`) assert que le RENDU
|
||
HTML CONTIENT ces 4 marqueurs — mais ils sont **HARDCODÉS dans le test**, une
|
||
copie de plus **jamais comparée à CLAUDE.md**. Si #4 change (accent
|
||
`#f0b429`→`#c9a227`) et qu'on aligne `branding.py`, l'oracle du test reste périmé
|
||
(rouge trompeur) ; si RIEN ne bouge, tout reste vert alors que le site public
|
||
`vente.otov7.com` émettrait la mauvaise couleur — invention silencieuse de la
|
||
classe même que #6 interdit, qu'aucune suite `tests/` (qui teste des FONCTIONS,
|
||
pas l'ancrage à CLAUDE.md) n'attrape.
|
||
- **État courant** : **aucun token périmé** — les 4 constantes + docstring + README
|
||
+ oracle du test recoupent CLAUDE.md #4 exactement (anti-invention #6, rien à
|
||
réécrire). Le défaut est la **surface ungated** (module hors CI).
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « Publiciste/branding »
|
||
avant `sys.exit`) : (0) **ancrage** — les 4 tokens recomputés de la ligne #4 de
|
||
CLAUDE.md (2 couleurs backtickées + 2 fontes de la parenthèse `(… + …)`) ; (a)
|
||
`branding.py` CONSTANTES == #4 (hex **insensible à la casse**, fontes à l'exact
|
||
strip) ; (b) **docstring** (texte AVANT les constantes) cite les 4 EXACTS ; (c)
|
||
README cite les 2 fontes ; (d) **oracle du test** — le tuple `for token in (...)`
|
||
== les 4 tokens de #4 (set-diff : absent ET en trop). Un claim absent échoue AUSSI
|
||
(traçabilité #6).
|
||
|
||
**6 morsures vérifiées** : `branding.py` accent `#f0b429→#e0a420` (constante
|
||
périmée) · `branding.py` display `Fraunces→Playfair Display` · **CLAUDE.md #4**
|
||
accent `→#c9a227` (mord **SIMULTANÉMENT** les 3 copies aval : constante + docstring
|
||
+ oracle du test — prouve la valeur de l'ANCRAGE) · docstring retire l'accent de la
|
||
prose (claim absent) · README échange une fonte (`Cormorant Garamond→Georgia`) ·
|
||
oracle du test retire `Fraunces` ; restauré = green : 4 tokens sains · constantes ==
|
||
#4 · docstring 4 tokens · README 2 fontes · oracle == #4 · exit 0. Working tree
|
||
byte-restauré (`git checkout --`, **JAMAIS** `git clean`, interdit absolu) ·
|
||
**7 gates re-verts**.
|
||
|
||
- `ci/README.md` (table récap du pipeline + paragraphe détaillé « Tokens design
|
||
canoniques `publiciste/branding` ancrés sur CLAUDE.md #4 ») 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 déploiement
|
||
réel du site `vente.otov7.com` reste côté VPS).
|
||
- **Auto-score 4Big** : 96/100.
|
||
|
||
## Session `20260801_030214` · Buffer S8 · Domaine Fiscal/e-CF DGII : l'**IDENTITÉ du RÔLE émetteur** e-CF — le SEUL rôle habilité à ÉMETTRE un comprobante fiscal électronique — nommé **en prose** par le bullet `role_id` du README (`fiscal/ecf_dgii/README.md:51-54`) via TROIS attributs data-derived (`role_id` `compta-fiscaliste-ecf` · `erpnext_role_name` `OTO Compta Fiscaliste eCF` · `portail` `compta`) était **HORS de tout gate d'IDENTITÉ**. Le bloc **e-NCF** gate le FORMAT de l'identifiant ; le bloc **cross-cohérence** (2e surface, session précédente) gate états émetteurs/champs de base/FormaPago ; mais AUCUN ne comparait l'IDENTITÉ de ce rôle à ses deux sources faisant autorité.
|
||
|
||
**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 d'IDENTITÉ**
|
||
que la Fiche Mobile / les Fiches Faisabilité (`agent-fiche-role-attrs-ungated`) ou les
|
||
gardes CRM/`workflow_vente` (rôle→transition) — appliquée à une **3e surface distincte
|
||
du MÊME README `fiscal/ecf_dgii`** : le rôle émetteur nommé dans la section
|
||
« Cross-cohérence », jamais gaté hors le FORMAT e-NCF et les ensembles états/champs.
|
||
|
||
**Dérive silencieuse fermée** :
|
||
- `05_deliverables_mvp/fiscal/ecf_dgii/README.md:53-54` — bullet `role_id` :
|
||
« **et** appartenir au portail `compta` — le rôle dédié `compta-fiscaliste-ecf`
|
||
(_OTO Compta Fiscaliste eCF_) ».
|
||
- Sources faisant autorité (double) : (a) `out/ecf_plan.json` (byte-gaté par
|
||
`check_artifacts`) — CHAQUE `emission_events[]` porte `role_id` +
|
||
`erpnext_role_name` ; (b) `rbac/rbac_50_roles.json` (contrat schéma-validé ·
|
||
les 3 MANIFEST RBAC en dérivent, byte-gatés) — le rôle d'`id`
|
||
`compta-fiscaliste-ecf` porte `portail: "compta"`. Le `portail` n'est PAS dans
|
||
l'artefact ; il n'est vérifiable QUE contre le contrat.
|
||
- Piège : la seule vérification existante (`tests/`) teste des **FONCTIONS**
|
||
(`ncf`/résolution RBAC), jamais la PROSE. RÉAFFECTER l'émission à un autre rôle
|
||
(dans le plan), le RENOMMER, ou DÉPLACER `compta-fiscaliste-ecf` du portail
|
||
`compta` vers un autre portail (dans le contrat : l'e-CF serait émis **HORS
|
||
Compta** — la séparation même que la cross-cohérence pose) laisse la prose périmée
|
||
EN SILENCE pendant que l'artefact/le contrat disent autre chose → l'agent ERPNext
|
||
Backend câblerait le mauvais rôle émetteur (le risque même que #6 interdit).
|
||
- **État courant** : **aucun attribut périmé** — les 3 recoupent artefact+contrat
|
||
exactement (anti-invention #6, rien à réécrire). Le défaut est la **surface
|
||
ungated**.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « Fiscal rôle émetteur »
|
||
après le bloc FormaPago) : (1) **par attribut** — `role_id`/`erpnext_role_name`
|
||
recomputés de `emission_events` + `portail`/nom Frappe recomputés du contrat, prose
|
||
EXACTE exigée (regex par claim) ; (2) **cohérences croisées bonus** (mordent un
|
||
plan/contrat INTERNEMENT incohérent) : tous les `emission_events` pointent UN SEUL
|
||
rôle (fonction · non vide) · ce `role_id` existe EXACTEMENT une fois au contrat · le
|
||
nom résolu dans l'artefact == le contrat (zéro-duplication) · `portail == compta`
|
||
(séparation des pouvoirs). Un claim absent échoue AUSSI (traçabilité #6).
|
||
|
||
**6 morsures vérifiées** : README renomme le `role_id`
|
||
(`compta-fiscaliste-ecf→…-xxx`, prose périmée) · README renomme le nom Frappe · README
|
||
déplace le portail (`compta→ventes`) · **artefact** réaffecte les DEUX
|
||
`emission_events` à `ventes-directeur` (5 mords en cascade : nom≠contrat + portail
|
||
HORS Compta + les 3 claims de prose) · **contrat** déplace le rôle vers `ventes`
|
||
(portail HORS Compta + prose portail périmée) · **artefact** diverge du contrat sur
|
||
le nom (`OTO Compta X`, zéro-dup cassé) ; restauré = green : 3 claims == artefact/
|
||
contrat · exit 0. Working tree byte-restauré (`git checkout --`, **JAMAIS**
|
||
`git clean`, interdit absolu) · **7 gates re-verts**.
|
||
|
||
- `ci/README.md` (table récap du pipeline + paragraphe détaillé « identité du RÔLE
|
||
émetteur e-CF, 3e surface du MÊME README `fiscal/ecf_dgii` ») 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
|
||
du rôle + le câblage Compupar restent côté VPS).
|
||
- **Auto-score 4Big** : 96/100.
|
||
|
||
## Session `20260801_033217` · Buffer S8 · Domaine Fiches agents/colonne « Job CI » : chaque table de livrables de `03_agents/*/AGENT.md` porte une colonne **Job CI** qui NOMME entre backticks le job Gitea Actions exécutant la suite du module (`rbac-tests`, `fiscal-ecf-tests`, `frontend-portails-tests`…). Ce nom est **saisi à la main** ; la source de vérité est la clé `jobs:` de `.gitea/workflows/ci.yml`. Ce lien n'était couvert par **AUCUN gate** : `check_ci_integrity.sh` prouve que chaque job est CÂBLÉ dans `gate.needs` (INV-A/B) et les blocs amont de `check_readme_claims` recomputent les **comptes** de tests par suite — mais RIEN ne vérifiait que le nom ÉCRIT dans la fiche DÉSIGNE un job réel.
|
||
|
||
**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 les cellules « Tests » par suite ou la colonne « Type » des Role
|
||
Profile — appliqué à la **surface data-derived distincte** : le NOM de job cité par
|
||
la fiche, jamais confronté à la définition réelle du workflow CI.
|
||
|
||
**Dérive silencieuse fermée** :
|
||
- `03_agents/*/AGENT.md` — colonne « Job CI » : 33 tokens `*-tests` répartis sur
|
||
13 fiches, chacun un nom de job Gitea Actions recopié à la main.
|
||
- Source faisant autorité : la section `jobs:` de `.gitea/workflows/ci.yml`.
|
||
- Piège : renommer un job dans ci.yml (`rbac-tests`→`rbac-role-tests`) ou mal
|
||
recopier `demo-scenario-tests` en `demo-scenarios-tests` laisse la fiche pointer
|
||
un job **fantôme** — le CI reste **vert** (le vrai job tourne sous son nouveau
|
||
nom), la doc d'identité de l'agent ment en silence. `check_ci_integrity` ne
|
||
regarde QUE le câblage `gate.needs`, aveugle au NOM cité par la prose.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, bloc « Fiches agents · colonne
|
||
Job CI ») : on RECOMPUTE l'ensemble des jobs depuis la clé `jobs:` de ci.yml
|
||
(la section `on:` — push/pull_request/workflow_dispatch — est AVANT `jobs:` et donc
|
||
naturellement exclue ; jamais une liste à la main) et on exige que chaque token
|
||
`*-tests` cité dans une fiche y figure. Direction **fiche→ci.yml** (le
|
||
consommateur) : ci.yml peut définir des jobs non cités (légitime), mais une fiche
|
||
ne peut pas citer un job absent. La disparition totale de la colonne échoue AUSSI
|
||
(le recensement qui s'évapore est lui-même une régression).
|
||
|
||
**Morsures vérifiées (2)** : (1) token de fiche `seo-tests`→`seo-suite-tests`
|
||
(job fantôme, mord les 2 citations dans `seo/AGENT.md`) ; (2) job ci.yml
|
||
`rbac-tests` renommé `rbac-role-tests` → mord **SIMULTANÉMENT les 4 fiches** qui
|
||
le citent (`erpnext_backend`/`ifc_speckle`/`mobile`/`rendu`). État courant :
|
||
**33 références == jobs ci.yml**, exit 0. Working tree byte-restauré
|
||
(`cp` depuis backup + `git checkout` implicite — **JAMAIS** `git clean`, interdit
|
||
absolu). Sibling gates re-verts : `check_ci_integrity`/`check_docs`/
|
||
`guard_constraints`/`check_artifacts` = PASS.
|
||
|
||
- `ci/README.md` (table récap du pipeline + paragraphe détaillé « Colonne Job CI
|
||
des fiches agents == section `jobs:` de ci.yml ») 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 ; aucun
|
||
déclenchement de workflow CI réel — analyse statique du YAML uniquement).
|
||
- **Auto-score 4Big** : 96/100.
|
||
|
||
## Session `20260801_040219` · Buffer S8 · Domaine CRM/DocType porteur : le **NOM du DocType porteur** `OTO Dossier Vente` — le **point d'attache** du pipeline vente (le Workflow s'y branche via son `document_type`) — était RECOPIÉ À LA MAIN à plusieurs endroits (titre + ligne « Nom du DocType » du README `crm/dossier_vente`, fiche `03_agents/crm/AGENT.md:25`, fiche `03_agents/onapi_legal/AGENT.md:50`) mais **AUCUN gate ne couvrait le NOM**. Le bloc CRM pipeline existant ne recompute que le **COMPTE d'états** (9), aveugle à l'identité ; aucune suite `tests/` (FONCTIONS de graphe) n'attrape la prose.
|
||
|
||
**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 d'IDENTITÉ
|
||
que les fiches de rôle RBAC (`agent-fiche-role-attrs-ungated`) ou l'identité du rôle
|
||
émetteur e-CF — appliquée à une **surface distincte** : le NOM d'un DocType porteur,
|
||
transcrit à ≥4 endroits et **asserté en prose égal** à `workflow.document_type`,
|
||
jamais confronté à l'artefact byte-gaté.
|
||
|
||
**Dérive silencieuse fermée** :
|
||
- `05_deliverables_mvp/crm/dossier_vente/README.md:1` (titre « # DocType porteur ·
|
||
`OTO Dossier Vente` ») **et** `:29` (« **Nom du DocType** = `document_type` du
|
||
workflow (`OTO Dossier Vente`) »).
|
||
- `03_agents/crm/AGENT.md:25` — « **DocType porteur** `OTO Dossier Vente` : … sans
|
||
lui le pipeline n'a rien à quoi s'attacher ».
|
||
- `03_agents/onapi_legal/AGENT.md:50` — « cible = `workflow.document_type`
|
||
(`OTO Dossier Vente`) ».
|
||
- Source faisant autorité : `crm/dossier_vente/out/doctype_oto_dossier_vente.json`
|
||
(champ `name`, = `MANIFEST.doctype_name`, byte-gaté par `check_artifacts`). L'attache
|
||
est confirmée par `crm/workflow_vente/out/workflow.json[0].document_type` (byte-gaté).
|
||
- Piège : **RENOMMER** le DocType dans `doctype_spec.json`
|
||
(`OTO Dossier Vente`→`OTO Dossier de Vente`) reconstruit les DEUX artefacts de
|
||
façon COHÉRENTE (le `document_type` du workflow suit, les tests restent verts),
|
||
MAIS les mentions en prose se PÉRIMENT en silence → l'agent ERPNext Backend
|
||
importerait un DocType sous un nom pendant que la fiche/le README en nomment un
|
||
autre, et le Workflow s'attacherait à un DocType FANTÔME (la rupture même que
|
||
« sans lui le pipeline n'a rien à quoi s'attacher » veut prévenir).
|
||
- **État courant** : **aucun nom périmé** — les 4 mentions + les 2 artefacts
|
||
recoupent exactement (anti-invention #6, rien à réécrire). Le défaut est la
|
||
**surface ungated**.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « CRM · IDENTITÉ du
|
||
DocType porteur » après le bloc CRM pipeline états) : on RECOMPUTE le nom depuis
|
||
`doctype_oto_dossier_vente.json[name]` et on exige que chaque prose le nomme EXACTEMENT
|
||
(regex backtické par mention). Cohérences croisées bonus (mordent un artefact
|
||
INTERNEMENT incohérent) : `DocType.name == MANIFEST.doctype_name` (cohérence interne)
|
||
· `== workflow.document_type` (**l'attache porteur↔workflow** — sinon le Workflow vise
|
||
un DocType fantôme). Un claim absent échoue AUSSI (traçabilité #6).
|
||
|
||
**7 morsures vérifiées** (3 côté artefact · 4 côté prose) : nom artefact renommé
|
||
(`name`→`OTO Dossier de Vente`) → mord **6 lignes en cascade** (incohérence interne +
|
||
attache rompue + les 4 mentions périmées) · `workflow.document_type` divergent →
|
||
**attache ROMPUE** seule · `MANIFEST.doctype_name` divergent → **incohérence interne**
|
||
seule · fiche `crm/AGENT.md:25` renommée · fiche `onapi_legal:50` renommée · ligne
|
||
« Nom du DocType » périmée · titre du README périmé ; restauré = green : 6 ✓ · 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` (ligne récap du pipeline + paragraphe détaillé « IDENTITÉ du DocType
|
||
porteur `crm/dossier_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 puis du Workflow reste côté agent ERPNext Backend / VPS).
|
||
- **Auto-score 4Big** : 96/100.
|
||
|
||
## Session `20260801_043224` · Buffer S8 · Fiches agents/citation CLAUDE.md #10 : la prose « Anti-invention » de DEUX fiches — `03_agents/erpnext_backend/AGENT.md:57` (devises **USD + DOP** · format **Letter US** · paiements **Cardnet**, pas Stripe) et `03_agents/crm/AGENT.md:37` (devises **USD + DOP** · format **Letter US**) — RECOPIE À LA MAIN les valeurs canoniques de la contrainte NON-NÉGOCIABLE CLAUDE.md #10 en AFFIRMANT « cités depuis CLAUDE.md #10, jamais réinventés », mais AUCUN gate ne liait cette citation à #10.
|
||
|
||
**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 bloc « paramètres canoniques » faisabilite/generator (model.py
|
||
CANONICAL ↔ #9/#10) et publiciste/branding (4 tokens ↔ #4) — appliqué à une SURFACE
|
||
distincte : la prose des fiches agents qui se réclame de #10.
|
||
|
||
**Dérive silencieuse fermée** :
|
||
- `03_agents/erpnext_backend/AGENT.md:57` + `03_agents/crm/AGENT.md:37` — prose
|
||
« Anti-invention (#6) » citant en gras les tokens #10 (devises/format/paiement).
|
||
- Source faisant autorité : la contrainte NON-NÉGOCIABLE **CLAUDE.md #10**
|
||
(`**USD + DOP**` devises · `**Letter US**` format · `**Cardnet**` paiements (pas Stripe)).
|
||
- Le seul contrôle amont (bloc « paramètres canoniques ») n'ancre QUE
|
||
`faisabilite/generator/genlib/model.py` CANONICAL + son README — AVEUGLE à ces fiches.
|
||
Aucune suite `tests/` (FONCTIONS, jamais l'ancre à CLAUDE.md) n'attrape la prose.
|
||
- Piège #6 : RENOMMER le prestataire (`Cardnet`→`Azul`) ou changer une devise/format dans
|
||
CLAUDE.md #10 laisse ces citations PÉRIMÉES en silence, contredisant le mandat pendant
|
||
que la prose se réclame de « CLAUDE.md #10 » — « vert trompeur ».
|
||
|
||
**Gate ajouté** (bloc « Fiches agents · CITATION des constantes CLAUDE.md #10 ») :
|
||
réutilise les valeurs `exp` déjà recomputées de #9/#10 (zéro duplication) + le prestataire
|
||
EXCLU (`(pas X)`) recomputé de #10 ; config table par-fiche du SOUS-ENSEMBLE de tokens
|
||
qu'elle cite (crm omet le paiement) ; exige de chaque fiche l'ancre « CLAUDE.md #10 » +
|
||
chaque token en gras `**<valeur>**` == #10 (casse tolérée) + l'exclusion pour erpnext.
|
||
|
||
**5 morsures vérifiées** (restauré vert après chacune, 7 gates re-verts) :
|
||
1. CLAUDE.md #10 `Cardnet`→`Azul` → mord erpnext SEUL (crm ne cite pas le paiement) ✓
|
||
2. CLAUDE.md #10 devises `USD + DOP`→`USD + EUR` → mord les DEUX fiches ✓
|
||
3. CLAUDE.md #10 `(pas Stripe)`→`(pas PayPal)` → mord l'exclusion erpnext ✓
|
||
4. fiche crm `Letter US`→`Letter USA` → mord CRM (format) ✓
|
||
5. fiche erpnext supprime l'ancre `CLAUDE.md #10` → mord INTROUVABLE ✓
|
||
|
||
## Session `20260801_050225` · Buffer S8 · SEO/schema.org · IDENTITÉ du nœud racine `Organization` ancrée sur CLAUDE.md §Entités : le nom de marque **`Helios RD`** (la *marque publique* · **CLAUDE.md §Entités** « **Helios RD** (marque publique) sous WAG ») et son `url` == base_url du site, portés par le nœud racine du graphe JSON-LD `05_deliverables_mvp/seo/out/seo_schema_org.json`, étaient **byte-gatés pour la REPRODUCTIBILITÉ** (`check_artifacts` prouve que l'artefact se reconstruit depuis `seo_spec.json`) mais **JAMAIS ANCRÉS à CLAUDE.md**. Le bloc SEO schema.org amont gate la COMPOSITION (`@type` + bijection un-listing-par-projet), aveugle à l'IDENTITÉ de la marque que Google indexe.
|
||
|
||
**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 les tokens branding
|
||
`publiciste/branding` ancrés sur CLAUDE.md #4 et les paramètres canoniques
|
||
`faisabilite/generator` ancrés sur #9/#10 (`CANONICAL` ↔ contrainte) — appliqué à
|
||
une **surface distincte** : l'identité de marque émise dans le JSON-LD **public** de
|
||
`vente.otov7.com`, dont la reproductibilité byte était gatée mais pas l'ancrage.
|
||
|
||
**Dérive silencieuse fermée** :
|
||
- `05_deliverables_mvp/seo/out/seo_schema_org.json` `@graph` — nœud racine
|
||
`Organization` : `name: "Helios RD"` + `url: "https://vente.otov7.com"` +
|
||
`@id: ".../#organization"` ; chaque nœud `Residence` (listing) porte
|
||
`brand.@id == org.@id` + `address.addressCountry == "DO"`.
|
||
- `05_deliverables_mvp/seo/seo_spec.json[schema_org]` — l'INPUT du générateur :
|
||
`organization.name == "Helios RD"`, `country_code == "DO"`, et un bullet `source`
|
||
qui DÉCLARE l'ancrage « CLAUDE.md §Entites (Helios RD = marque publique sous WAG) ».
|
||
- Source faisant autorité : **CLAUDE.md §Entités** (« **Helios RD** (marque publique)
|
||
sous WAG ») pour la marque + `MANIFEST.base_url` (byte-gaté) pour le base_url + le
|
||
`country_code` du spec pour le géo-ciblage.
|
||
- Piège #6 : la byte-gate prouve que l'artefact se reconstruit **fidèlement… depuis
|
||
un spec dérivé**, PAS que le nom == CLAUDE.md. RENOMMER la marque dans
|
||
`seo_spec.json` (ou dans CLAUDE.md §Entités) puis régénérer laisse le JSON-LD
|
||
**public** émettre un nom de marque qui CONTREDIT le mandat pendant que
|
||
`check_artifacts` reste **VERT** — « vert trompeur ». Aucune suite `tests/`
|
||
(FONCTIONS de génération, jamais l'ancrage à CLAUDE.md) ne l'attrape.
|
||
- **État courant** : aucune valeur périmée — `name`/`url`/`@id`/rattachements/
|
||
`addressCountry`/déclaration du spec recoupent CLAUDE.md+MANIFEST+spec exactement
|
||
(anti-invention #6, rien à réécrire). Le défaut est la **surface ungated**.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « SEO org » après le bloc
|
||
composition schema.org) : la marque est **re-dérivée** de CLAUDE.md §Entités
|
||
(parenthèse « (marque publique) », exigée présente 1× exactement · zéro duplication),
|
||
puis : **(a)** `Organization.name` == marque CLAUDE.md ; **(b)** `Organization.url`
|
||
== `MANIFEST.base_url` et `@id` sous base_url ; **(c)** chaque listing rattaché à
|
||
CETTE Organization (`brand.@id` == `org.@id`) et sous base_url ; **(d)**
|
||
`addressCountry` UNIFORME == `country_code` du spec (code ISO 2 lettres, géo-ciblage
|
||
appliqué à l'identique) ; **(e)** le spec DÉCLARE l'ancrage (`organization.name` ==
|
||
marque + `source` cite CLAUDE.md + la marque). Racine `Organization` exigée singleton
|
||
en pré-requis.
|
||
|
||
**6 morsures vérifiées** (restauré vert après chacune, 7 gates re-verts) :
|
||
1. **artefact** `Organization.name`→`Helios DR` → mord (a) anchor ✓
|
||
2. **CLAUDE.md §Entités** rename `Helios RD`→`Helios Dominicana` → mord
|
||
**SIMULTANÉMENT** (a) artefact ET (e) spec — l'ancre est vive ✓
|
||
3. **artefact** `Organization.url` divergent → mord (b) url/@id ✓
|
||
4. **artefact** un `addressCountry` `DO`→`US` → mord (d) uniformité géo ✓
|
||
5. **spec** `organization.source` supprime l'ancre CLAUDE.md → mord (e) déclaration ✓
|
||
6. **artefact** un listing `brand.@id`→`#phantom` → mord (c) rattachement ✓
|
||
|
||
Restauré = green : `Helios RD` == CLAUDE.md · url == base_url · 9 listings rattachés
|
||
sous base_url · addressCountry uniforme `DO` · spec déclare l'ancrage · 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é
|
||
« Identité du nœud racine `Organization` 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 ; l'injection
|
||
réelle du `<script type="application/ld+json">` dans les pages `vente.otov7.com`
|
||
reste côté agent Frontend/SEO / VPS).
|
||
- **Auto-score 4Big** : 96/100.
|
||
|
||
## Session `20260801_053228` · Buffer S8 · Legal/CONFOTUR · IDENTITÉ des ENTITÉS porteuses ancrée sur CLAUDE.md §Entités : le champ Select **`entite_porteuse`** du DocType `CONFOTUR Application` — le **menu déroulant** qui fixe QUELLE entité juridique porte chaque dossier d'incitation touristique — offre pour options les **7 entités canoniques** du mandat (`WAF` · `WA SRL` · `AC Arias Cuevas` · `Consortium ECR DR` · `Helios RD` · `Ploutos` · `9060 QC`). Ces options sont DÉRIVÉES via `options_source` de `confotur_spec.json[entites]` et **byte-gatées pour la REPRODUCTIBILITÉ** par `check_artifacts` (l'artefact `doctype_confotur_application.json` se reconstruit depuis le spec) mais **JAMAIS ANCRÉES** à CLAUDE.md §Entités, leur source faisant autorité. Les blocs CONFOTUR amont ne gatent que le COMPTE de champs/rôles et la cross-cohérence des PERMISSIONS, AVEUGLES à l'identité de cette liste d'entités.
|
||
|
||
**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 la marque SEO org
|
||
ancrée sur CLAUDE.md §Entités (session précédente `050225`) et les tokens branding
|
||
`publiciste/branding` ancrés sur #4 — appliqué à une **surface distincte du MÊME
|
||
§Entités** : le déroulant que l'agent ERPNext Backend importe (`bench
|
||
import-fixtures`), dont la reproductibilité byte était gatée mais pas l'ancrage.
|
||
|
||
**Dérive silencieuse fermée** :
|
||
- `05_deliverables_mvp/legal/confotur/out/doctype_confotur_application.json` — champ
|
||
`entite_porteuse` (Select · `reqd`), `options` = les 7 entités séparées par `\n`.
|
||
- `05_deliverables_mvp/legal/confotur/confotur_spec.json` — l'INPUT : `entites` (la
|
||
liste) + `entites_source` (DÉCLARE « CLAUDE.md #Entites (…) ») + le champ dont
|
||
`options_source: "entites"` (wiring : le déroulant DÉRIVE de la liste).
|
||
- `05_deliverables_mvp/legal/confotur/README.md:37-38` — prose « `entite_porteuse`
|
||
(Select) : entités CLAUDE.md (WAF · WA SRL · **AC** · Consortium ECR DR · Helios
|
||
RD · Ploutos · 9060 QC) » (abréviation éditoriale « AC » pour « AC Arias Cuevas »).
|
||
- Source faisant autorité : **CLAUDE.md §Entités** — les 7 tokens en gras
|
||
`**...**` de la section « ## Entités », dans l'ordre.
|
||
- Piège #6 : la byte-gate prouve que l'artefact se reconstruit **fidèlement… depuis
|
||
un spec dérivé**, PAS que la liste == CLAUDE.md. RENOMMER une entité dans
|
||
CLAUDE.md §Entités (`9060 QC`→`9061 QC`) OU dans `confotur_spec.json[entites]`
|
||
puis régénérer laisse le DocType offrir un menu qui CONTREDIT/omet une entité
|
||
canonique du mandat pendant que `check_artifacts` reste VERT — « vert trompeur ».
|
||
L'agent ERPNext Backend importerait un DocType dont le déroulant liste une entité
|
||
FANTÔME ou en omet une (la restriction même que `reqd` pose). Aucune suite
|
||
`tests/` (FONCTIONS de génération) ne l'attrape.
|
||
- **État courant** : aucune valeur périmée — options artefact / `entites` du spec /
|
||
déclaration / README recoupent CLAUDE.md §Entités exactement (anti-invention #6,
|
||
rien à réécrire). Le défaut est la **surface ungated**.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « Legal · CONFOTUR —
|
||
IDENTITÉ des ENTITÉS porteuses » avant `sys.exit`) : les 7 entités sont
|
||
**re-dérivées** des tokens en gras de la section « ## Entités » de CLAUDE.md
|
||
(uniques, non vides), puis : **(a)** options du Select de l'artefact == entités
|
||
CLAUDE.md (ORDRE exact) ; **(b)** `spec[entites]` == CLAUDE.md (l'INPUT ancré) ;
|
||
**(c)** le spec DÉCLARE l'ancrage (`entites_source` cite CLAUDE.md + chaque
|
||
entité) ; **(d)** wiring intact (`options_source == "entites"` — sinon le déroulant
|
||
est DÉCROCHÉ de la liste ancrée) ; **(e)** README cite l'ancre « CLAUDE.md » +
|
||
nomme chaque entité par **nom complet OU 1er token** (l'abréviation « AC » tolérée ·
|
||
#6, pas de réécriture d'une prose correcte pour satisfaire un gate). Un claim absent
|
||
échoue AUSSI (traçabilité #6).
|
||
|
||
**7 morsures vérifiées** (restauré vert après chacune, 7 gates re-verts) :
|
||
1. **CLAUDE.md §Entités** `9060 QC`→`9061 QC` → mord SIMULTANÉMENT (a) artefact +
|
||
(b) spec + (c) déclaration + (e) README = **4 bites** — l'ancre est vive ✓
|
||
2. **artefact** retire la dernière option → mord (a) ✓
|
||
3. **artefact** permute 2 options → mord (a) ORDRE ✓
|
||
4. **spec** `entites` renomme `Ploutos`→`Ploutos SA` → mord (b) ✓
|
||
5. **spec** `entites_source` retire « CLAUDE.md » → mord (c) ✓
|
||
6. **spec** champ `options_source`→`hardcoded` → mord (d) wiring ✓
|
||
7. **README** retire l'entité `Ploutos` de la ligne → mord (e) ✓
|
||
|
||
Restauré = green : options == CLAUDE.md (7, ordre) · spec == CLAUDE.md · spec
|
||
déclare l'ancrage · wiring == "entites" · README nomme les 7 · 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é
|
||
« Entités porteuses du DocType CONFOTUR ancrées 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 ; l'import réel
|
||
du DocType `CONFOTUR Application` reste côté agent ERPNext Backend / VPS).
|
||
- **Auto-score 4Big** : 96/100.
|