Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 85a9ba7fca | |||
| 28e5e2145b | |||
| 290bd2ec02 | |||
| 9963279879 | |||
| 8667cdf4de | |||
| 915be3cb77 | |||
| 2e627a6a3b | |||
| c6428533a0 | |||
| 64238f6833 | |||
| dce409f0f6 | |||
| c4487fdcf7 | |||
| 71999e04c4 | |||
| 7e8bb9a0de | |||
| 7ce0f7b1e7 |
@@ -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
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user