7e674f2f09
La SPEC RBAC a déjà 3 gates (§3 ventilation par portail · §4 colonne Entité · §7 run-book) mais son §5 « Les N rôles (résumé) » restait AVEUGLE. Ce §5 restitue le total de rôles sur 3 surfaces prose : le compte-titre « ## 5. Les 50 rôles », NEUF comptes parenthétiques de regroupement (`**Ventes (8)**` …) et une ligne d'auto-contrôle arithmétique `_(6+8+4+7+5+8+4+2+6 = 50.)_`. AJOUTER un rôle au contrat (|rôles| 50→51) laisse titre/somme/`= 50` périmés en silence ; ÉDITER `Ventes (8)→(7)` sans toucher la ligne casse la cohérence titre↔somme — ni les suites tests/ (FONCTIONS RBAC, jamais la prose) ni le byte-gate d'artefact ne l'attrapent. Subtilité (memory rbac-spec-portail-count-gate : « §5 by-family is editorial ») : les 9 regroupements sont ÉDITORIAUX (« Direction / Gouvernance » agrège des rôles de familles direction+finance ≠ colonne `famille`) ⇒ AUCUN mapping groupe↔famille (dérive fausse). On gate UNIQUEMENT ce qui est data-derived sans ce mapping. Nouveau bloc « 1septies) §5 » : on recompute N = |rôles| du contrat (rbac_50_roles.json, byte-gaté) et on exige (a) compte-titre == N ; (b) les 9 comptes de regroupement == EXACTEMENT dans l'ordre les termes de la ligne (comparaison de LISTES ⇒ mord valeur ET position) ; (c) Σ termes == membre droit ; (d) membre droit == N. Section/titre/ligne/comptes absents = échec (#6). 5 morsures vérifiées (working tree byte-restauré via git checkout -- <fichier>, gate rc=1 sous mutation · rc=0 restauré) : titre 50→51 (a) · Ventes (8)→(9) (b) · = 50.→= 49. (c+d double) · terme 6+→7+ (b+c double) · suppression ligne (absence). ci/README.md : row check-readme-claims étendue de la clause §5. 7 gates re-verts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2614 lines
203 KiB
Markdown
2614 lines
203 KiB
Markdown
# Activity Log · 2026-08-01 · Claude Code DTP
|
||
|
||
## Session `20260801_230453` · Buffer S8 · QA/Audit 5D : gate d'IDENTITÉ du **VERDICT GLOBAL** — le LABEL de conclusion de l'audit
|
||
|
||
**Tâche** : **Sprint 8 · buffer** (DevOps CI/CD · QA). Roadmap fonctionnellement
|
||
complète ; la valeur marginale reste le durcissement anti-dérive (CLAUDE.md #6)
|
||
des surfaces prose data-derived encore non gatées. Les 7 gates verts au départ.
|
||
|
||
**Surface trouvée (aveugle)** : le **verdict global** de l'audit 5D — le LABEL
|
||
`PASS` / `PASS_WITH_OPEN_ITEMS` / `FAIL` qui est la **HEADLINE de tout le
|
||
livrable** — était **HORS de tout gate**. Le bloc audit_5d existant recompute la
|
||
**ventilation chiffrée** (« **13 PASS · 0 FAIL · 4 à confirmer** », pass/fail/à
|
||
confirmer d'`audit_report.totals`), l'énumération des open items, les 5 dimensions
|
||
et les livrables audités — mais **jamais le label de verdict lui-même**. Or ce
|
||
label est data-derived (`out/audit_report.json[verdict]` == `out/MANIFEST.json[verdict]`
|
||
== `PASS_WITH_OPEN_ITEMS`, byte-gatés), recalculé par `qalib/builder.py` via la
|
||
règle **`FAIL` si ≥1 FAIL · sinon `PASS_WITH_OPEN_ITEMS` si ≥1 A_CONFIRMER · sinon
|
||
`PASS`**, et transcrit à la main à **deux** endroits du README : la RÈGLE
|
||
(`Verdict global : …`, l.39) et le VERDICT COURANT (`## Verdict courant` l.49). Le
|
||
risque #6 est le **CŒUR** du livrable : écrire `PASS` alors que 4 params
|
||
réglementaires restent `a_confirmer` = l'audit « force » un PASS et prétend propre
|
||
un dossier qui ne l'est pas — exactement ce que l'audit reproche aux autres. La
|
||
ventilation chiffrée peut rester exacte pendant que le label ment ; ni
|
||
`check_artifacts` (byte only) ni les suites `tests/` (FONCTIONS, pas la prose)
|
||
n'attrapent.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, bloc « Audit 5D · IDENTITÉ du VERDICT
|
||
GLOBAL », inséré juste après la ventilation chiffrée) : on **MIROITE** la règle du
|
||
générateur et on exige — **(a)** HONNÊTETÉ interne : `audit_report.verdict` ==
|
||
verdict recalculé du triplet (fail>0→FAIL · a_confirmer>0→PASS_WITH_OPEN_ITEMS ·
|
||
sinon PASS) ⇒ un verdict FORCÉ incohérent avec ses propres chiffres est mordu ;
|
||
**(b)** CROISÉ : `MANIFEST.verdict` == `audit_report.verdict` ; **(c)** le LABEL
|
||
« Verdict courant » du README == `audit_report.verdict` (ancré sur le heading +
|
||
la ventilation `**N PASS`, pas sur la règle l.39) ; **(d)** la RÈGLE « Verdict
|
||
global » énumère EXACTEMENT le **codomaine** `{FAIL, PASS_WITH_OPEN_ITEMS, PASS}`
|
||
du générateur (un label renommé côté `builder.py` sans mise à jour de la règle est
|
||
mordu). Claim absent = échec (disparition = régression).
|
||
|
||
**5 morsures vérifiées** (working tree byte-restauré après chacune via
|
||
`git checkout -- <chemin précis>`, JAMAIS `git checkout .` / `git clean`) :
|
||
1. README label `PASS_WITH_OPEN_ITEMS`→`PASS` → ✗ (c) « Verdict courant = PASS MAIS
|
||
artefact PASS_WITH_OPEN_ITEMS » ;
|
||
2. README règle l.39 `PASS_WITH_OPEN_ITEMS`→`PASS_OPEN` → ✗ (d) « codomaine
|
||
[FAIL, PASS, PASS_OPEN] ≠ [FAIL, PASS, PASS_WITH_OPEN_ITEMS] » ;
|
||
3. `audit_report.verdict`→`PASS` (à_confirmer reste 4) → ✗ (a) « verdict FORCÉ,
|
||
incohérent — viole #6 » **+** cascade ✗ (b) + ✗ (c) (3 fails : l'honnêteté est
|
||
la morsure la plus forte) ;
|
||
4. `MANIFEST.verdict`→`PASS` → ✗ (b) « MANIFEST PASS MAIS audit_report
|
||
PASS_WITH_OPEN_ITEMS (désynchronisés) » ;
|
||
5. README label supprimé (l.49) → ✗ (c) « Verdict courant INTROUVABLE ».
|
||
Restauré = green : (a)(b)(c)(d) tous ✓.
|
||
|
||
- `ci/README.md` : la sous-clause « audit 5D » de la row `check-readme-claims`
|
||
étendue de l'IDENTITÉ du VERDICT GLOBAL (label + honnêteté interne + croisé
|
||
MANIFEST + codomaine de la règle documentée).
|
||
- **7 gates re-verts** : `guard_constraints` / `validate_json` / `check_docs` /
|
||
`check_artifacts` / `check_regression` / `check_ci_integrity` /
|
||
`check_readme_claims` (tous rc=0).
|
||
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ;
|
||
édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact).
|
||
- **Auto-score 4Big** : 96/100.
|
||
|
||
## Session `20260801_210434` · Buffer S8 · Domaine CRM/commissions : l'**IDENTITÉ du CHAMP DEVISE** — la clause `devise_field` = le champ `devise` (`Select` **USD/DOP** · #10) du DocType Dossier Vente, transcrite **en prose** dans la §« Cross-cohérence » (`crm/commissions/README.md:54`) — était **HORS de tout gate**. Les trois blocs commissions déjà en place gatent les états soumis / brouillons exclus, les champs de base Currency, l'identité des rôles payés (portail `ventes`) et la promesse anti-taux #6 — mais **aucun** ne touchait ce couple champ↔devises (l'**invariant CLI #8** du générateur).
|
||
|
||
**Tâche** : **Sprint 8 · buffer** (DevOps CI/CD · QA). Roadmap fonctionnellement
|
||
close ; poursuite de la série anti-dérive (CLAUDE.md #6). **4e surface distincte du
|
||
MÊME livrable `crm/commissions`** (après états/champs, anti-taux, rôles payés) — le
|
||
« **sur quelle devise** » du calcul `base × taux`. Surface DISTINCTE du bloc « Fiches
|
||
#10 » (prose ERPNext/CRM) et du bloc e-CF `TipoMoneda` (options du spec fiscal) :
|
||
ici c'est le champ `devise` du DocType Dossier Vente + la clé racine `devise_field`
|
||
du plan, ancrés sur **CLAUDE.md #10** (« **USD + DOP** devises »).
|
||
|
||
**Dérive silencieuse fermée** :
|
||
- `05_deliverables_mvp/crm/commissions/README.md:54` — clause « `devise_field` = le
|
||
champ `devise` (`Select` **USD/DOP** · #10) du DocType » : NOMME le champ (`devise`),
|
||
son fieldtype (`Select`), ses options (`USD/DOP`) et tague `#10`.
|
||
- Sources faisant autorité : (a) `out/commission_plan.json` (byte-gaté par
|
||
`check_artifacts`) — clé RACINE `devise_field` (le champ du Dossier Vente qui porte
|
||
la devise d'un montant commissionné) ; (b) `crm/dossier_vente/doctype_spec.json`
|
||
(l'INPUT du générateur) — le champ `devise` de fieldtype `Select` + ses `options` ;
|
||
(c) **CLAUDE.md #10** (ligne 24 : « **USD + DOP** devises ») — les 2 devises
|
||
canoniques. L'invariant CLI #8 (`commissions_gen.py:151-158`) exige les trois
|
||
simultanément ; le gate re-dérive INDÉPENDAMMENT.
|
||
- Piège : la seule vérification existante (`tests/`) teste des **FONCTIONS**
|
||
finance/résolution, jamais la PROSE vs l'artefact. RENOMMER le champ (`devise`→
|
||
`moneda`), changer son fieldtype (`Select`→`Data` : énumération PERDUE, montant en
|
||
monnaie libre) ou altérer ses options (`USD/DOP`→`USD/EUR` : devise HORS #10 → le
|
||
calcul commissionnerait dans une monnaie non prévue) laisse la prose périmée EN
|
||
SILENCE. `devise_field` n'apparaissait NULLE PART dans `ci/` (grep = 0).
|
||
- **État courant** : **aucune valeur périmée** — `plan.devise_field == "devise"` ==
|
||
champ DocType `devise` (`Select`, options `[USD, DOP]`) == CLAUDE.md #10 ; la prose
|
||
README recoupe tout exactement (anti-invention #6, rien à réécrire). Le défaut est
|
||
la **surface ungated**.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « CRM/Commissions ·
|
||
IDENTITÉ du CHAMP DEVISE » après le bloc rôles payés) : (0) **ANCRAGE** — les 2
|
||
devises depuis le segment gras « USD + DOP » de CLAUDE.md #10 ; (a) **CROSS-ARTEFACT**
|
||
— `plan.devise_field == "devise"` == fieldname du champ DocType, fieldtype `Select`,
|
||
`options` (ORDRE inclus) == devises #10 ; (b) **PROSE** — README:54 nomme le champ
|
||
`devise`, le dit `Select`, cite `USD/DOP` == #10 et tague `#10`. Un claim absent
|
||
échoue AUSSI (traçabilité #6). Direction plan/DocType/#10 → README.
|
||
|
||
**7 morsures vérifiées** (4 côté README · 1 DocType · 1 CLAUDE.md · 1 plan) : README
|
||
champ `devise→moneda` (prose périmée) · README `Select→Data` (type périmé) · README
|
||
`USD/DOP→USD/EUR` (devises périmées) · README tag `· #10` retiré (claim INTROUVABLE) ·
|
||
**DocType** `options USD→EUR` (hors #10, cross-artefact) · **CLAUDE.md #10**
|
||
`USD+DOP→USD+EUR` (ancrage VIVANT · mord le cross-artefact) · **plan** `devise_field
|
||
devise→moneda` (invariant #8) ; restauré = green : plan.devise_field == DocType ==
|
||
#10 · prose == artefacts · exit 0. Working tree byte-restauré (`git checkout --`,
|
||
**JAMAIS** `git clean`, interdit absolu) · **7 gates re-verts**
|
||
(`check_readme_claims`/`check_ci_integrity`/`check_docs`/`guard_constraints`/
|
||
`check_artifacts`/`check_regression`/`validate_json`).
|
||
|
||
- `ci/README.md` (clause de la table récap du pipeline + paragraphe détaillé
|
||
« Quatrième surface du MÊME livrable `crm/commissions` · identité du champ devise »)
|
||
mis à jour.
|
||
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ;
|
||
édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact ; la création du
|
||
champ `devise` du DocType `bench` reste côté VPS).
|
||
- **Auto-score 4Big** : 96/100.
|
||
|
||
## Session `20260801_183412` · Buffer S8 · Domaine Legal/CONFOTUR : les **deux champs du DocType `CONFOTUR Application` DÉRIVÉS du pipeline vente** — `dossier_vente` (Link → cible `workflow.document_type` = `OTO Dossier Vente`) et `estado` (Select → options = les états `confotur_*` du **workflow vente**), transcrits **en prose** dans la §« Cœur du livrable : cross-cohérence » (`legal/confotur/README.md:33-36`) — étaient **HORS de tout gate d'IDENTITÉ**. Les blocs CONFOTUR amont gatent le **compte** de champs/rôles, la cross-cohérence des **PERMISSIONS** (rôle→actions) et l'ancrage des **ENTITÉS** (`entite_porteuse`→CLAUDE.md §Entités) — mais **aucun** ne touchait `dossier_vente`/`estado`.
|
||
|
||
**Tâche** : **Sprint 8 · buffer** (DevOps CI/CD · QA). Roadmap fonctionnellement
|
||
close ; poursuite de la série anti-dérive (CLAUDE.md #6). **Même classe EXACTE** que
|
||
l'ancrage `entite_porteuse` du MÊME README (session `20260801` entités CONFOTUR) —
|
||
appliqué à la **surface distincte** des deux AUTRES champs data-derived du même
|
||
DocType, jamais gatés hors comptes/permissions/entités.
|
||
|
||
**Dérive silencieuse fermée** :
|
||
- `05_deliverables_mvp/legal/confotur/README.md:35-36` — bullet `dossier_vente`
|
||
(Link) : « cible = `workflow.document_type` (= `OTO Dossier Vente`) » (valeur
|
||
NOMMÉE en clair) · `:33-34` — bullet `estado` (Select) : « options = les états
|
||
`confotur_*` du **workflow vente** … dérivées — jamais réécrites en dur ».
|
||
- Source faisant autorité : `crm/workflow_vente/workflow_vente_spec.json` — l'**INPUT**
|
||
que le builder confotur LIT (déclaré `workflow_source` dans le MANIFEST), d'où il
|
||
tire `document_type` (= cible du Link) et les `update_value` d'états commençant par
|
||
`confotur` (= options du Select). L'artefact `out/doctype_confotur_application.json`
|
||
porte `dossier_vente.options`/`estado.options`, byte-gaté par `check_artifacts`.
|
||
- Piège : la seule vérification existante (`tests/`) teste des **FONCTIONS** de
|
||
génération, jamais la PROSE du README. RENOMMER le DocType porteur côté workflow
|
||
(`OTO Dossier Vente`→`OTO Dossier de Vente`) ou AJOUTER/RENOMMER un état CONFOTUR
|
||
régénère l'artefact confotur (`check_artifacts` reste VERT — il se reconstruit
|
||
depuis le spec workflow) MAIS laisse la prose README « (= `OTO Dossier Vente`) »
|
||
périmée EN SILENCE → l'agent ONAPI/Legal câblerait une cible de Link / un menu
|
||
d'états que le pipeline ne porte plus (l'invention #6 interdit).
|
||
- **État courant** : **aucune valeur périmée** — `dossier_vente` Link → « OTO
|
||
Dossier Vente » == workflow `document_type` · `estado` options
|
||
`[confotur_depose, confotur_approuve]` == états `confotur_*` du workflow (ordre
|
||
pipeline) · la prose README recoupe l'artefact/le workflow exactement
|
||
(anti-invention #6, rien à réécrire). Le défaut est la **surface ungated**.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « Confotur champs
|
||
dérivés » avant `sys.exit`) : (0) **traçabilité** — MANIFEST `workflow_source` cite
|
||
bien `crm/workflow_vente/workflow_vente_spec.json` ; (a) **cross-artefact** —
|
||
`dossier_vente.options` (Link) == workflow `document_type` ; (b) **prose** — la
|
||
valeur README « (= `…`) » == workflow `document_type` ; (c) **cross-artefact** —
|
||
`estado.options` == `update_value` des états `confotur_*` du workflow (ORDRE
|
||
pipeline) ; (c′) toute option `estado` porte le préfixe `confotur` (le README
|
||
l'affirme) ; (d) **prose** — le bullet `estado` DÉCLARE la dérivation (`confotur_*`
|
||
+ workflow vente). Un claim absent échoue AUSSI (traçabilité #6). Direction
|
||
README/artefact→workflow spec.
|
||
|
||
**6 morsures vérifiées** : README `dossier_vente` valeur `OTO Dossier Vente→OTO
|
||
Dossier de Vente` (prose périmée, b) · README bullet `estado` supprimé (claim
|
||
INTROUVABLE, d) · **artefact** `estado.options` +`brouillon` (options ≠ workflow, c
|
||
ET hors-préfixe, c′) · **artefact** `dossier_vente.options→OTO Autre Doc` (Link ≠
|
||
document_type, a) · **MANIFEST** `workflow_source` déréférencé (dérivation non
|
||
déclarée, 0) · **spec workflow** `document_type→OTO Dossier de Vente` (mord
|
||
SIMULTANÉMENT l'artefact a ET la prose b — l'ancre est vive) ; restauré = green :
|
||
6 checks verts · exit 0. Working tree byte-restauré (`git checkout --`, **JAMAIS**
|
||
`git clean`, interdit absolu) · **7 gates re-verts**
|
||
(`check_readme_claims`/`check_ci_integrity`/`check_docs`/`guard_constraints`/
|
||
`check_artifacts`/`check_regression`/`validate_json`).
|
||
|
||
- `ci/README.md` (clause de la table récap du pipeline + paragraphe détaillé
|
||
« Champs `dossier_vente`/`estado` du DocType CONFOTUR dérivés du workflow vente »)
|
||
mis à jour.
|
||
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ;
|
||
édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact ; l'import réel
|
||
du DocType `bench import-fixtures` reste côté VPS).
|
||
- **Auto-score 4Big** : 96/100.
|
||
|
||
## Session `20260801_173409` · Buffer S8 · Fiches agents/pied de page voix OTOIA : le **PIED DE PAGE CANONIQUE** « - Voix **Amélie QC** (**multilingual_v2**) pour toute interaction OTOIA » — répété à l'identique dans **11 des 13 fiches** `03_agents/*/AGENT.md` (toutes sauf `faisabilite`/`publiciste`, qui n'orchestrent pas OTOIA) — RECOPIE À LA MAIN les **trois** tokens de la contrainte **CLAUDE.md §Architecture cible** « Voix Amélie QC (multilingual_v2) » : prénom persona (`Amélie`), qualificatif de marque (`QC`), modèle de voix ElevenLabs (`multilingual_v2`). Cette surface était **HORS de tout gate**.
|
||
|
||
**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 persona/capabilities
|
||
`frontend/chat_otoia` ancrées sur §Architecture cible, la marque SEO org (§Entités) et
|
||
les tokens branding (#4) — appliqué à une **surface distincte encore ungated** : les 11
|
||
pieds de page de fiches, jamais confrontés à CLAUDE.md.
|
||
|
||
**Dérive silencieuse fermée** :
|
||
- `03_agents/{bim,crm,devops,erpnext_backend,frontend_console,ifc_speckle,mobile,
|
||
onapi_legal,qa,rendu,seo}/AGENT.md` — ligne « - Voix Amélie QC (multilingual_v2)
|
||
pour toute interaction OTOIA ».
|
||
- Source faisant autorité : **CLAUDE.md §Architecture cible** (ligne 30) « Voix
|
||
Amélie QC (multilingual_v2) ».
|
||
- Piège : le bloc « Chat OTOIA · IDENTITÉ de la persona » RE-DÉRIVE bien persona+voix
|
||
de CLAUDE.md mais ne confronte QUE le module `chat_otoia` (spec + `out/` + son propre
|
||
README + l'oracle du test) — **AVEUGLE aux 11 pieds de page** ; le bloc « configs
|
||
runtime » note même explicitement que le « QC » est « gardé en littéral — non gaté
|
||
ici ». Le jour où ElevenLabs déprécie `multilingual_v2` (→`multilingual_v3`) ou qu'on
|
||
renomme la persona/le qualificatif dans CLAUDE.md, ces 11 fiches annonceraient EN
|
||
SILENCE une voix/persona que le mandat ne prescrit plus, chacune affirmant orchestrer
|
||
« toute interaction OTOIA » avec le mauvais modèle. Aucune suite `tests/` (FONCTIONS
|
||
du builder, jamais la prose des fiches) n'attrape ce « vert trompeur ».
|
||
- **État courant** : **aucune valeur périmée** — les 11 pieds de page recoupent
|
||
CLAUDE.md §Architecture cible 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 · pied de
|
||
page voix » après le bloc persona/capabilities Chat OTOIA) : on RE-DÉRIVE le triplet
|
||
(persona · qualif · voix) de CLAUDE.md §Architecture cible **INDÉPENDAMMENT** du parse
|
||
persona amont (dont la regex fige `QC` en dur — un renommage du qualificatif doit mordre
|
||
ICI, pas seulement via l'INTROUVABLE du bloc persona) ; pour CHAQUE fiche git-trackée
|
||
portant le pied de page, on exige ses **3 tokens == CLAUDE.md**. Une fiche avec prose
|
||
« interaction OTOIA » mais pied de page malformé échoue ; la **disparition totale** du
|
||
recensement échoue AUSSI (surface évaporée = régression #6). Direction fiche→CLAUDE.md.
|
||
|
||
**6 morsures vérifiées** : CLAUDE.md `multilingual_v2→v3` (mord les 11 fiches +, en
|
||
cascade, le bloc persona amont — 15 fails, l'ancre est vive) · CLAUDE.md qualif `QC→BC`
|
||
(mord les 11 pieds de page — bloc INDÉPENDANT, ne s'appuie pas sur le parse persona qui
|
||
fige QC) · fiche `seo` voix `v2→v1` (1 fail) · fiche `crm` persona `Amélie→Sophie`
|
||
(1 fail) · fiche `bim` pied de page malformé, parenthèses retirées (1 fail INTROUVABLE) ·
|
||
pied de page retiré de TOUTES les fiches (surface évaporée, 1 fail) ; restauré = green :
|
||
11 pieds de page == CLAUDE.md · 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é « Pied de
|
||
page Voix Amélie QC (multilingual_v2) des fiches agents ancré sur CLAUDE.md
|
||
§Architecture cible ») 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 runtime réel
|
||
de la voix OTOIA reste côté VPS).
|
||
- **Auto-score 4Big** : 96/100.
|
||
|
||
## Session `20260801_170404` · Buffer S8 · Domaine QA/audit_4big : la colonne **« Fait vérifié »** de la table du barème (`qa/audit_4big/README.md:24,26`) — les **DEUX SEUILS STRUCTURELS** que le scorer applique réellement (`DOC` « ≥ **400 octets** » · `TESTS` « ≥ **8 méthodes** `def test_*` »), transcrits À LA MAIN — étaient **HORS de tout gate**. Le bloc BARÈME existant recompute la 2e colonne (**poids** : DOC 20 · CONTRAT 20 · TESTS 25 · CLI 15 · HANDOFF 20) mais son regex (`| \`DOC\` | 20 |`) est **AVEUGLE à la 3e cellule** ; ces deux nombres sont DATA-DERIVED de `quality_spec.json[thresholds]` (`min_doc_bytes`/`min_test_methods`).
|
||
|
||
**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 le
|
||
bloc BARÈME (poids) et la carte de renormalisation par archétype du MÊME README —
|
||
appliqué à une **3e surface distincte du MÊME README `qa/audit_4big`** : les seuils
|
||
de la colonne « Fait vérifié », jamais gatés hors leur poids voisin.
|
||
|
||
**Dérive silencieuse fermée** :
|
||
- `05_deliverables_mvp/qa/audit_4big/README.md:24` — cellule DOC « un `*.md`
|
||
(README/SPEC) ≥ **400 octets** à la racine » (= `thresholds.min_doc_bytes`) ·
|
||
`:26` — cellule TESTS « ≥ **8 méthodes** `def test_*` dans `tests/` » (=
|
||
`thresholds.min_test_methods`).
|
||
- Source faisant autorité : `05_deliverables_mvp/qa/audit_4big/quality_spec.json`
|
||
`[thresholds]` — l'**INPUT** du générateur, byte-gaté indirectement par
|
||
`check_artifacts` qui prouve que `out/quality_report.json` se **rebuild** depuis
|
||
lui. Les seuils ne sont PAS émis en clair dans le report ; seule l'**evidence
|
||
`TESTS`** de chaque module porte « (seuil N) » (émise par `crit_tests`), ce qui en
|
||
fait un **ancrage croisé** du seuil réellement APPLIQUÉ.
|
||
- Piège : la seule vérification existante (`tests/`) teste des **FONCTIONS** de
|
||
scoring (`crit_doc`/`crit_tests` avec seuil paramétré), jamais la PROSE du README.
|
||
Élever `min_test_methods` (8→12 : un module de 8-11 tests tomberait sous `FAIL` —
|
||
la barre même que le critère pose) ou changer `min_doc_bytes` dans le spec rebuild
|
||
le report SANS TOUCHER au README → prose « ≥ 8 méthodes »/« ≥ 400 octets » périmée
|
||
en silence, l'auditeur documentant un barème qu'il **n'applique plus** (l'invention
|
||
#6 interdit).
|
||
- **État courant** : **aucun seuil périmé** — les 2 cellules recoupent le spec
|
||
exactement, et le seuil TESTS appliqué (evidence report) == spec sur les 22 modules
|
||
(anti-invention #6, rien à réécrire). Le défaut est la **surface ungated**.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « README audit_4big ·
|
||
SEUILS STRUCTURELS » après le bloc renormalisation par archétype) : (1) **cross-check
|
||
d'ancrage** — le seuil `TESTS` APPLIQUÉ (regex `(seuil N)` sur l'evidence de CHAQUE
|
||
module du report) == `spec.min_test_methods` pour tous (mord une divergence
|
||
spec↔report) ; (2) **prose DOC** — cellule « ≥ N octets » recomputée du spec, exacte ;
|
||
(3) **prose TESTS** — cellule « ≥ N méthodes » recomputée du spec, exacte. Un claim
|
||
absent échoue AUSSI (traçabilité #6).
|
||
|
||
**5 morsures vérifiées** : README `DOC 400→450` (prose périmée) · README `TESTS 8→10`
|
||
(prose périmée) · README ligne DOC vidée du seuil (claim INTROUVABLE) · **spec**
|
||
`min_test_methods 8→12` + rebuild (README périmé ET cross-check evidence[8]≠spec[12]
|
||
spec↔report) · **spec** `min_doc_bytes 400→500` + rebuild (README DOC périmé) ;
|
||
restauré = green : seuil TESTS appliqué == spec (8) · DOC « ≥ 400 octets » == spec ·
|
||
TESTS « ≥ 8 méthodes » == spec · 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 `check-readme-claims` + paragraphe détaillé
|
||
« troisième surface · colonne Fait vérifié / seuils structurels ») 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 branchement
|
||
du gate 4Big sur le pipeline de release reste côté VPS).
|
||
- **Auto-score 4Big** : 96/100.
|
||
|
||
## 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.
|
||
|
||
## Session `20260801_060234` · Buffer S8 · Legal/CONFOTUR — cross-cohérence PERMISSIONS répliquée dans la FICHE AGENT `03_agents/onapi_legal/AGENT.md:43-45` : la MÊME table role→portail→actions que le README du module, jamais gatée
|
||
|
||
**Tâche** : **Sprint 8 · buffer** (DevOps CI/CD · QA). Roadmap fonctionnellement
|
||
close ; poursuite de la série anti-dérive (CLAUDE.md #6). Classe explicitement
|
||
notée en mémoire (`agent-fiche-role-attrs-ungated`) : une fiche `03_agents/*/AGENT.md`
|
||
peut restituer la MÊME donnée data-derived qu'un README déjà gaté, sur une **surface
|
||
distincte** que le gate README ne voit pas.
|
||
|
||
**Dérive silencieuse fermée** :
|
||
- `03_agents/onapi_legal/AGENT.md:43-45` — §Permissions de la fiche de l'agent
|
||
ONAPI/Legal, annoncée « **mot pour mot** les `permissions_cibles` RBAC des 3
|
||
rôles » : `ventes-confotur` (portail Ventes) → read/write/create/print ·
|
||
`legal-onapi` (portail Direction) → read/write/create · `legal-directeur`
|
||
(portail Direction) → read/write/**submit**/report. Convention DIFFÉRENTE du
|
||
README du module (` / ` espacé · `**submit**` en gras).
|
||
- Source faisant autorité : `legal/confotur/out/MANIFEST.json`
|
||
(`roles_rbac_utilises[]` : `role_id` · `portail` · `permissions`, recomputé du
|
||
contrat `rbac_50_roles.json` à chaque build · byte-gaté par check_artifacts).
|
||
- Piège : le bloc « Legal confotur perms · cross-cohérence » existant ne gate QUE
|
||
la §Permissions du README du MODULE (`legal/confotur/README.md`), AVEUGLE à la
|
||
fiche. PROMOUVOIR `ventes-confotur` à `submit` (élévation de privilège · casse
|
||
la séparation des pouvoirs dont `is_submittable` est déduit), RETIRER une
|
||
action, RÉAFFECTER un portail, RENOMMER un rôle ou AJOUTER une ligne fantôme
|
||
dans la FICHE laisse celle-ci périmée pendant que l'artefact (et le README, lui
|
||
gaté) disent autre chose → l'agent ONAPI/Legal lirait sa PROPRE doc d'identité
|
||
mentant sur le jeu de permissions qu'il porte (le risque même que « mot pour
|
||
mot » promet d'écarter) — « vert trompeur » qu'aucune suite `tests/` (FONCTIONS
|
||
de mapping RBAC, jamais la prose de fiche) n'attrape.
|
||
- **État courant** : **aucune ligne périmée** — les 3 couples portée→actions et
|
||
leurs portails recoupent l'artefact exactement (anti-invention #6). Le défaut
|
||
est la **surface ungated**.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « Legal · CONFOTUR —
|
||
cross-cohérence PERMISSIONS répliquée dans la FICHE AGENT » après le bloc
|
||
cross-cohérence du README) : RÉUTILISE `conf_perm_by_id`/`conf_portail_by_id`
|
||
déjà dérivés du MANIFEST par le bloc README (ZÉRO duplication du contrat #6) ;
|
||
regex tolérant l'espacement ` / ` et le gras `**` ; PAR rôle le portail (casse
|
||
normalisée) ET le jeu d'actions exigés EXACTS (set-diff : absent ET en trop) ;
|
||
identité d'ensemble fiche⇔MANIFEST (aucun rôle fantôme ni manquant). Un claim
|
||
absent échoue AUSSI (traçabilité).
|
||
|
||
**6 morsures vérifiées** : fiche promeut `ventes-confotur`→`submit` (en trop) ·
|
||
fiche retire `create` de `legal-onapi` (absent) · fiche réaffecte portail
|
||
Ventes→Direction · fiche renomme `legal-directeur`→`legal-boss` (ligne
|
||
INTROUVABLE + ensemble absents/fantôme) · sous-liste entière supprimée (3
|
||
INTROUVABLES + ensemble vide) · **artefact** promeut `ventes-confotur`→`submit`,
|
||
fiche stale (le vrai « silent green » : source change, fiche fige) ; restauré =
|
||
green : 3 rôles portail+actions == MANIFEST · identité d'ensemble · exit 0.
|
||
Working tree byte-restauré (`git checkout --`, **JAMAIS** `git clean`) · **7
|
||
gates re-verts**.
|
||
|
||
- `ci/README.md` (table récap du pipeline · clause « 3e surface » CONFOTUR) 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_063234` · Buffer S8 · Chat OTOIA/persona+capabilities : l'**IDENTITÉ de la persona** (`Amélie` / voix `multilingual_v2`) et des **4 CAPABILITIES OTOIA** (`aec.py` + `knowledge.py` + `prompt_engine.py` + `chat.py`) du montage Chat par portail `frontend/chat_otoia` — portées par `out/chat_mount.json` (5 configs runtime) + `out/MANIFEST.json`, **byte-gatés pour la reproductibilité** (`check_artifacts` prouve la reconstruction depuis `chat_spec.json`) mais **JAMAIS ANCRÉES à CLAUDE.md §Architecture cible**, leur source faisant autorité (« Voix Amélie QC (multilingual_v2) » l.30 · « OTOIA capabilities : aec.py + knowledge.py + prompt_engine.py + chat.py » l.28).
|
||
|
||
**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, la marque SEO `Organization` ancrée sur
|
||
§Entités, les paramètres canoniques `faisabilite/generator` ancrés sur #9/#10 —
|
||
appliqué à une **surface distincte** : la persona + les capabilities émises dans CHAQUE
|
||
portail rôle, dont la reproductibilité byte était gatée mais pas l'ancrage.
|
||
|
||
**Dérive silencieuse fermée** :
|
||
- `05_deliverables_mvp/frontend/chat_otoia/out/chat_mount.json` — les 5 mounts,
|
||
chacun `persona: {nom: "Amélie", voix: "multilingual_v2"}` +
|
||
`capabilities: [aec.py, knowledge.py, prompt_engine.py, chat.py]` ; idem
|
||
`out/MANIFEST.json` ; l'INPUT `chat_spec.json` (persona + `capabilities[].module`) ;
|
||
la table README:28-29 (« Persona `Amélie` / `multilingual_v2` » · « Capabilities
|
||
`aec/knowledge/prompt_engine/chat.py` » toutes deux annotées « `CLAUDE.md
|
||
§Architecture cible` »).
|
||
- Source faisant autorité : **CLAUDE.md §Architecture cible** (l.28 capabilities ·
|
||
l.30 « Voix Amélie QC (multilingual_v2) »).
|
||
- Piège #6 : le SEUL contrôle d'identité vit dans `tests/`
|
||
(`test_persona_amelie_sourcee` / `test_capabilities_sourcees_sans_ajout`) mais son
|
||
**ORACLE est HARDCODÉ** (`self.assertEqual(spec["persona"]["nom"], "Amélie")` ·
|
||
`caps == ["aec.py", …]`) — une **copie de plus, jamais comparée à CLAUDE.md**.
|
||
Renommer la persona (`Amélie`→`Sophie`), changer la voix, ou renommer/RETIRER une
|
||
capability dans `chat_spec.json` (ou dans CLAUDE.md) reconstruit l'artefact
|
||
fidèlement (byte-gate VERTE) sans toucher l'oracle du test (tests VERTS) → le chat
|
||
monté dans CHAQUE portail annoncerait une persona/voix/capabilities qui CONTREDIT le
|
||
mandat — « vert trompeur » qu'aucune suite `tests/` (FONCTIONS de génération, jamais
|
||
l'ancre à CLAUDE.md) n'attrape.
|
||
- **État courant** : **aucune valeur périmée** — persona + capabilities recoupent
|
||
CLAUDE.md exactement (anti-invention #6, rien à réécrire). Le défaut est l'**ancrage
|
||
ungated**.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « Chat OTOIA · persona +
|
||
capabilities ancrées CLAUDE.md » avant `sys.exit`) : persona+capabilities re-dérivées
|
||
de CLAUDE.md §Architecture cible (regex `Voix … QC (…)` + `OTOIA capabilities : …`
|
||
split `+`, NFC), puis : **(a)** `chat_spec.json` persona/capabilities == CLAUDE.md
|
||
(ordre exact) ; **(b)** le spec DÉCLARE l'ancrage (`persona.source` + chaque
|
||
`capability.source` citent CLAUDE.md) ; **(c)** CHACUN des 5 mounts + le MANIFEST
|
||
portent persona/capabilities == CLAUDE.md ; **(d)** README cite l'ancre §Architecture
|
||
cible + nomme persona (`Amélie`+`multilingual_v2`) + chaque capability (notation
|
||
compacte matchée par radical) ; **(e)** l'ORACLE du test == CLAUDE.md (set-diff). Un
|
||
claim absent échoue AUSSI (traçabilité #6).
|
||
|
||
**8 morsures vérifiées** : CLAUDE.md `Amélie→Sophie` (mord SIMULTANÉMENT
|
||
spec/mount/README/oracle — 5 fails, l'ancre est vive) · CLAUDE.md retire `chat.py`
|
||
(3 fails) · spec `aec.py→exfil.py` (**capability injectée**) · spec `persona.source`
|
||
sans CLAUDE.md · **UN SEUL** mount à voix altérée (byte-gate aveugle) · README retire
|
||
le radical `prompt_engine` · oracle du test persona `Amélie→Bob` · oracle du test
|
||
capabilities réordonné/tronqué ; restauré = green : 5 sous-checks (a/b/c/d/e) verts ·
|
||
exit 0. Working tree byte-restauré (`git checkout --`, **JAMAIS** `git clean`, interdit
|
||
absolu) · **7 gates re-verts** · unittest module `chat_otoia` re-vert.
|
||
|
||
- `ci/README.md` (table récap du pipeline + paragraphe détaillé « Persona &
|
||
capabilities du Chat OTOIA ancrées sur CLAUDE.md §Architecture cible ») 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 des
|
||
`Custom Block` + le câblage de l'endpoint OTOIA restent côté agent ERPNext Backend / VPS).
|
||
- **Auto-score 4Big** : 96/100.
|
||
|
||
## Session `20260801_070237` · Buffer S8 · Mobile/app_config : le **SDK MAJEUR Expo** (« Expo 54 ») — la version majeure que l'app compagnon cible, **ANCRÉE sur la roadmap Sprint 5 l.56** « Rebuild Expo 54 » — était transcrite À LA MAIN **cinq fois** dans le README SANS AUCUN gate d'IDENTITÉ ni d'ANCRAGE. Le bloc « Mobile · récap de l'app Expo » existant ne gate QUE le quadruplet onglets/rôles/langues/identifiants `a_confirmer` (comptes de `MANIFEST.counts`), AVEUGLE au majeur du SDK.
|
||
|
||
**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'ANCRAGE que
|
||
la marque SEO §Entités (`Helios RD` ancrée sur CLAUDE.md) ou les `roadmap_line` de la
|
||
recette `qa/acceptance` — appliqué ici à une valeur ancrée sur la **ROADMAP** (source
|
||
faisant autorité de la version Expo, CLAUDE.md ne la spécifiant pas).
|
||
|
||
**Dérive silencieuse fermée** :
|
||
- `05_deliverables_mvp/mobile/app_config/README.md:4/19/35/72` — « Expo 54 »
|
||
(×4 mentions) + la ligne de source « Expo SDK **54** | roadmap Sprint 5 l.56 ».
|
||
- Chaîne de dérivation (byte-gatée par `check_artifacts`) : roadmap Sprint 5 l.56
|
||
« Rebuild Expo 54 » (INPUT faisant autorité) → `mobile_spec.json[app]`
|
||
(`expo_sdk_major = 54` + `expo_sdk_source` cite la roadmap) →
|
||
`out/app_config.json.expo.extra.expoSdkMajor = 54` + `out/MANIFEST.json
|
||
.app.expo_sdk_major = 54`.
|
||
- Piège #6 : BUMPER le SDK (Expo 55 sort · `eas build` cible 55) dans le
|
||
spec/artefact SANS toucher au README (ou l'inverse), ou DÉRIVER la roadmap de
|
||
l'artefact, laisse la prose PÉRIMÉE en silence pendant que le graphe byte-gaté
|
||
dit autre chose → l'agent Mobile lancerait `eas build` sur le MAUVAIS SDK (le
|
||
rebuild même que la ligne roadmap prescrit). Aucune suite `tests/` (FONCTIONS de
|
||
génération, jamais la prose ni l'ancre à la roadmap) n'attrape ce « vert
|
||
trompeur ».
|
||
- **État courant** : aucune mention périmée — les 4 « Expo 54 », le spec, le
|
||
MANIFEST et la roadmap concordent (anti-invention #6, rien à réécrire). Le défaut
|
||
est la **surface ungated + non ancrée à la roadmap**.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « Mobile · le SDK MAJEUR
|
||
Expo … ANCRÉ sur la roadmap Sprint 5 l.56 », inséré avant `sys.exit`) : le majeur
|
||
`sdk` re-dérivé de `app_config.json` byte-gaté, puis (a) **cohérence interne**
|
||
artefact ⇔ `MANIFEST.app.expo_sdk_major` ⇔ `spec.app.expo_sdk_major` ; (b)
|
||
**ancrage roadmap** — `re.search("Rebuild Expo (\d+)")` sur
|
||
`04_roadmap/ROADMAP_8_WEEKS_OR_LESS.md` == `sdk` (ancre vive) ; (c) le spec DÉCLARE
|
||
l'ancrage (`expo_sdk_source` cite « roadmap » + « Expo N ») ; (d) **toute** mention
|
||
prose « Expo [SDK] N » (regex `Expo(?:\s+SDK)?\s*\*{0,2}(\d+)`) == `sdk` (aucune
|
||
périmée) ; (e) la ligne de source du tableau cite l'ancre `roadmap` à côté du majeur.
|
||
Un claim absent échoue AUSSI (traçabilité).
|
||
|
||
**7 morsures vérifiées** : README `Expo SDK **54**→**55**` (prose périmée) ·
|
||
**artefact** `54→55` (README + roadmap périmés = le vrai *silent green*) ·
|
||
**roadmap** `Rebuild Expo 54→55` (artefact périmé · ancre qui dérive) ·
|
||
**MANIFEST** `54→55` (chaîne interne incohérente) · spec `expo_sdk_source` sans
|
||
« roadmap » (ancrage déclaré manquant) · README ligne de source sans « roadmap »
|
||
(ancrage prose manquant) · README retire TOUTE mention « Expo N » (claim absent) ;
|
||
restauré = green : cohérence interne · ancre roadmap vive · spec déclare · 4
|
||
mentions == 54 · ligne source cite roadmap · 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é « SDK majeur Expo
|
||
de l'app mobile ancré sur la roadmap Sprint 5 l.56 ») 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 ; `eas build`/
|
||
`eas submit` App Store #32 / Play Store restent côté agent Mobile · VPS).
|
||
- **Auto-score 4Big** : 96/100.
|
||
|
||
## Session `20260801_073239` · Buffer S8 · Fiscal/ecf_dgii : le JEU DE DEVISES `moneda` (`USD`/`DOP`) ANCRÉ sur CLAUDE.md #10 — le champ que `TipoMoneda` porte dans chaque e-CF DGII, byte-gaté pour la REPRODUCTIBILITÉ mais JAMAIS ancré à la contrainte. Le README s'en réclame « #10 » (README:37/57/77) ; les blocs fiscaux amont gatent le FormaPago (Cardnet · #10) + états/champs émetteurs, AVEUGLES au jeu de devises ; le bloc « Fiches #10 » ancre les fiches ERPNext/CRM, AVEUGLE au module fiscal.
|
||
|
||
**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'ANCRAGE que
|
||
les paramètres canoniques `faisabilite/generator` (#9/#10), la marque SEO §Entités
|
||
(`Helios RD`), la persona Chat OTOIA (§Architecture cible) ou les tokens branding #4
|
||
— appliqué ici à une valeur ancrée sur **CLAUDE.md #10** (« **USD + DOP** devises »).
|
||
|
||
**Dérive silencieuse fermée** :
|
||
- `05_deliverables_mvp/fiscal/ecf_dgii` — `moneda.options = ["USD","DOP"]` dans le
|
||
spec ET (byte-gaté par `check_artifacts`) dans `out/ecf_plan.json`.
|
||
- Chaîne de dérivation : CLAUDE.md #10 « **USD + DOP** devises » (INPUT faisant
|
||
autorité) → `ecf_spec.json[moneda].options` → `out/ecf_plan.json.moneda.options`
|
||
(copie verbatim par `ecflib/builder.py:79`).
|
||
- Piège #6 : **byte-gater une constante prouve la REPRODUCTIBILITÉ, PAS l'ANCRAGE**.
|
||
`check_artifacts` prouve `ecf_plan==build` depuis le spec, JAMAIS que
|
||
`spec[moneda]==#10`. RENOMMER/ÉTENDRE les devises dans CLAUDE.md #10
|
||
(`USD + DOP`→`USD + EUR`, ou +`EUR`) ET dans le spec de façon cohérente reste
|
||
byte-VERT tout en faisant émettre à l'e-CF un `TipoMoneda` d'une devise qui
|
||
CONTREDIT le mandat — « vert trompeur » qu'aucune suite `tests/` (FONCTIONS de
|
||
génération, jamais l'ancre à CLAUDE.md) n'attrape. L'agent ERPNext Backend
|
||
câblerait `Encabezado.TipoMoneda` sur une devise hors mandat (le risque même que
|
||
la cross-cohérence e-CF veut prévenir).
|
||
- **État courant** : aucune dérive — spec/artefact/README concordent avec #10
|
||
(anti-invention #6). Le défaut était la **surface byte-gatée mais non ancrée** +
|
||
l'absence de déclaration d'ancrage (`moneda.source` legitimement `null` car il
|
||
source le **taux TipoCambio**, à confirmer côté VPS · #8 — l'ancre des OPTIONS
|
||
devait vivre dans un champ DISTINCT).
|
||
|
||
**Correctif de déclaration** : nouveau champ `moneda.options_source` dans
|
||
`ecf_spec.json` (+ requis dans `ecf.schema.json`) citant « CLAUDE.md #10 — devises
|
||
**USD + DOP** » ; distinct de `source` (taux, reste `null`/`a_confirmer`). Artefact
|
||
`out/ecf_plan.json` régénéré (byte-gate re-verte · 39 tests module verts · le compte
|
||
`a_confirmer` inchangé, il ne teste que `moneda.a_confirmer`).
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, bloc « Fiscal · ecf_dgii — le JEU DE
|
||
DEVISES `moneda` … ANCRÉ sur CLAUDE.md #10 », inséré après la famille « Fiches
|
||
#10 ») : RÉUTILISE `exp["devises"]` déjà recomputé depuis #10 (zéro duplication),
|
||
`want_cur = re.findall("[A-Z]{3}", exp["devises"])`, puis (a) `spec[moneda].options`
|
||
== `want_cur` ORDRE EXACT (mord ajout/retrait/réordonnancement) ; (b) l'artefact
|
||
byte-gaté idem ; (c) `moneda.options_source` cite `CLAUDE.md #10` + chaque devise ;
|
||
(d1) la ligne README « devise (…USD/DOP… · #10) » cite le jeu EXACT ; (d2) toute
|
||
mention `moneda`/`devise` d'une paire de devises (scan ancré sur le contexte, évite
|
||
le faux positif `RNC / ITBIS`) == le jeu (aucune périmée). Un claim absent échoue
|
||
AUSSI (traçabilité).
|
||
|
||
**7 morsures vérifiées** : CLAUDE.md #10 `USD+DOP→USD+EUR` (l'ancre est vive →
|
||
mord a+b+c+d1 en cascade) · **spec** `options USD/DOP→USD/EUR` régénéré (le vrai
|
||
*silent green* → mord a) · **artefact** seul `USD→EUR` (spec+README périmés → mord
|
||
b) · **reorder** spec `[DOP,USD]` (ordre exact → mord a) · `options_source` sans
|
||
« CLAUDE.md #10 » (ancre déclarée manquante → mord c) · **README** ligne #10
|
||
`USD/DOP→USD/EUR` (prose périmée → mord d1) · **README** table `moneda USD/DOP→
|
||
USD/EUR` (mention périmée → mord d2) ; restauré = vert (a+b+c+d, 3 mentions
|
||
README concordantes · exit 0). Working tree byte-restauré (`git checkout --`,
|
||
**JAMAIS** `git clean`) · **7 gates re-verts** (validate_json · check_artifacts ·
|
||
check_readme_claims · check_docs · guard_constraints · check_ci_integrity ·
|
||
check_regression).
|
||
|
||
- `ci/README.md` (clause de récap du pipeline + paragraphe détaillé « jeu de
|
||
DEVISES `moneda` … 4e surface du MÊME README, ancré sur #10 ») mis à jour.
|
||
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ;
|
||
la confirmation du **TipoCambio** USD→DOP + l'émission réelle via Compupar
|
||
restent côté agent ERPNext Backend / Compta · VPS).
|
||
- **Auto-score 4Big** : 96/100.
|
||
|
||
---
|
||
|
||
## Session `20260801_080244` · Buffer S8 · SEO/projets_master — IDENTITÉ du CATALOGUE PROJETS (P01..P09) ANCRÉE sur CLAUDE.md §Projets
|
||
|
||
**Domaine** : SEO · `05_deliverables_mvp/seo/fixtures/projets_master.json` + artefacts `out/seo_schema_org.json`.
|
||
|
||
**Constat (anti-drift #6)** : la matière première du worker SEO est `projets_master.json` — 9 projets (`code` + `nom`) que le bundle SEO expose à Google (un listing `Residence` par projet dans le JSON-LD indexé sous `vente.otov7.com/projets/…`). Chaque entrée **DÉCLARE** `source.fichiers: ["CLAUDE.md#Projets"]` et le README:80 l'affirme (« 9 projets P01..P09 · noms sourcés de `CLAUDE.md §Projets` ») — **MAIS AUCUN gate ne le VÉRIFIAIT**. `check_artifacts` prouve QUE les `out/` se reconstruisent depuis la fixture (byte-for-byte), PAS que ses `code`/`nom` == CLAUDE.md §Projets ; les blocs SEO amont gatent les COMPTES de mots-clés, la COMPOSITION du graphe schema.org (bijection un-listing-par-projet) et l'IDENTITÉ de la marque racine `Helios RD` (§Entités) — tous AVEUGLES à l'identité de la **liste de projets**.
|
||
|
||
**Vert trompeur** : RENOMMER un projet dans CLAUDE.md §Projets (« Aqua Terra Las Terrenas » → « … Bay »), le RENOMMER/AJOUTER/RETIRER dans la fixture, puis régénérer, laisse le JSON-LD PUBLIC lister un projet FANTÔME ou en OMETTRE un canonique pendant que la byte-gate reste VERTE (l'artefact se reconstruit fidèlement… depuis une fixture DÉRIVÉE) et que le compte reste 9. Même classe EXACTE que la marque org (§Entités · session 050225), les entités CONFOTUR (§Entités · 053228) et les libellés projets du run_sheet démo (§Projets · déjà gaté). Aucune suite `tests/` (FONCTIONS de génération, jamais l'ancrage à CLAUDE.md) ne l'attrape.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, bloc « SEO · l'IDENTITÉ du CATALOGUE PROJETS ») — RE-DÉRIVE le catalogue de CLAUDE.md §Projets (zéro duplication · source unique), puis exige :
|
||
- **(a)** identité d'ensemble des codes fixture ⇔ CLAUDE.md §Projets (aucun fantôme/manquant · pas de doublon) ;
|
||
- **(b)** `nom` de chaque entrée == CLAUDE.md §Projets (accent-**insensible** : la fixture normalise « Xamaná »→« Xamana ») ;
|
||
- **(c)** chaque entrée DÉCLARE l'ancrage `CLAUDE.md#Projets` dans `source.fichiers` ;
|
||
- **(d)** les `name` des listings du graphe schema.org PUBLIC == l'ensemble des noms CLAUDE.md §Projets + bijection (1 listing/projet) + `MANIFEST.counts` projects/listings == |§Projets| ;
|
||
- **(e)** le README cite l'ancrage CLAUDE.md §Projets. Un claim absent échoue AUSSI (disparition = régression).
|
||
|
||
**Vérif** : 13 confirmations vertes (9 noms + set codes + listings schema.org + MANIFEST + README) ; **7 morsures vérifiées puis restaurées** — CLAUDE.md rename P07 (bite b+d simultanés, l'ancre est vive) · fixture nom drift P03 · fixture drop P09 (set manquant) · fixture add fantôme P10 (set fantôme) · fixture strip `source.fichiers` P01 · artefact schema.org rename `Structure`→`Estructura` (le vrai silent-green public) · README retire la citation §Projets. Restauré vert. **7 gates CI re-verts** (`check_readme_claims`, `check_artifacts`, `check_docs`, `check_ci_integrity`, `guard_constraints`, `check_regression`, `validate_json`).
|
||
|
||
- **Auto-score 4Big** : 96/100.
|
||
|
||
## Session `20260801_083254` · Buffer S8 · DevOps/deploy_runbook — IMPUTABILITÉ des CONFIRMATIONS préalables VPS (owner + réf-contrôle) ancrée sur out/deploy_runbook.json ⨯ qa/audit_5d
|
||
|
||
**Domaine** : DevOps · `05_deliverables_mvp/devops/deploy_runbook/README.md:55-58`
|
||
(ligne « Confirmations préalables VPS ») ⨯ `out/deploy_runbook.json` (byte-gaté) ⨯
|
||
`05_deliverables_mvp/qa/audit_5d/out/audit_report.json` (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 classe de « vert
|
||
trompeur » que la table run-book (2e surface `deploy_runbook`) ou l'identité du RÔLE
|
||
émetteur e-CF — appliquée à une **3e surface distincte du MÊME README `deploy_runbook`** :
|
||
non plus le COMPTE de confirmations ni l'ENSEMBLE de leurs noms (déjà gatés), mais
|
||
l'**imputabilité** de chacune — QUI la porte et QUEL contrôle audit_5d la trace.
|
||
|
||
**Dérive silencieuse fermée** :
|
||
- `devops/deploy_runbook/README.md:55-58` — la ligne « Confirmations préalables VPS »
|
||
annote CHAQUE confirmation réglementaire d'un COUPLE data-derived
|
||
`nom` (**owner** · **réf-contrôle**) : `taux_commission` (Direction · audit_5d D1.1) ·
|
||
`rnc_emisor` (Compta · D1.2) · `itbis_tipocambio` (Fiscaliste eCF · D1.3) ·
|
||
`seuil_uaf` (Oficial de Cumplimiento · D2.3) · `endpoint_otoia` (ERPNext Backend ·
|
||
session 19). L'OWNER = qui doit confirmer le paramètre avant déploiement ; la RÉF =
|
||
le contrôle qa/audit_5d (`D#.#`) — ou la session — qui en assure le suivi.
|
||
- Sources faisant autorité (double, byte-gatées par `check_artifacts`) : (a) les
|
||
`confirmations` de `out/deploy_runbook.json` — chaque item porte `owner` + `source` ;
|
||
(b) `qa/audit_5d/out/audit_report.json` — chaque `control` (`D1.1`..`D5.3`) porte
|
||
SON PROPRE `owner`. L'owner et le contrôle ne vivent PAS dans le MANIFEST
|
||
(`confirmations_ouvertes` n'est qu'une liste de NOMS) : ils ne sont vérifiables que
|
||
contre ces deux artefacts croisés.
|
||
- Piège #6 : les blocs DevOps amont ne gatent que le COMPTE (2) et l'ENSEMBLE des
|
||
noms — AVEUGLES à QUI porte chaque confirmation et à QUEL contrôle la trace.
|
||
RÉAFFECTER `taux_commission` de Direction à Ventes (mauvais responsable escaladé →
|
||
confirmation jamais obtenue · séparation des pouvoirs cassée), MISATTRIBUER le
|
||
contrôle (`D1.1`→`D2.1`, pointant un contrôle qui ne trace PAS ce paramètre) ou
|
||
citer un contrôle FANTÔME (`D9.9`, absent d'audit_5d) laisse la prose périmée EN
|
||
SILENCE pendant que l'artefact/l'audit disent autre chose → l'agent DevOps
|
||
escaladerait le mauvais owner / le mauvais contrôle. Aucune suite `tests/`
|
||
(FONCTIONS de graphe) n'attrape ce « vert trompeur ».
|
||
- **État courant** : aucune annotation périmée — les 5 couples owner/réf recoupent
|
||
`deploy_runbook.json`, et les 4 réf `D#.#` existent dans audit_5d avec un owner
|
||
compatible (`Oficial de Cumplimiento` ⊆ `Oficial de Cumplimiento / UAF`, `Fiscaliste
|
||
eCF` ⊂ `Compta Fiscaliste eCF`) (anti-invention #6, rien à réécrire). Le défaut est
|
||
la **surface ungated**.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, bloc « 2ter) deploy_runbook —
|
||
IMPUTABILITÉ des CONFIRMATIONS » après le bloc table de phases) : confirmations
|
||
aplaties de `deploy_runbook.json` (unicité inter-phases), contrôle audit_5d résolu de
|
||
la `source` (`D#.#`), annotations README parsées `nom` (owner · réf), puis **(a)**
|
||
cross-module — chaque confirmation tracée porte un contrôle audit_5d RÉEL dont l'owner
|
||
est COMPATIBLE avec l'owner run-book (sous-ensemble de tokens · tolère « / UAF ») ET
|
||
elle reste ANNOTÉE au README ; **(b)** chaque annotation désigne une confirmation
|
||
RÉELLE (pas fantôme), owner README ⊆ owner run-book (tolère l'abréviation), et la réf
|
||
== contrôle réel (présent audit_5d + source) ou, pour une réf textuelle
|
||
(« session 19 »), présente dans la source. Un claim absent échoue AUSSI (traçabilité #6).
|
||
|
||
**9 morsures vérifiées** (4 côté README · 3 côté artefact · 2 réf) : README réaffecte
|
||
owner `Direction→Ventes` (mauvais responsable) · README misattribue `D1.1→D2.1`
|
||
(contrôle périmé vs source) · README cite contrôle fantôme `D9.9` · README retire
|
||
l'annotation de `taux_commission` (tracée par D1.1, INTROUVABLE) · **artefact**
|
||
réaffecte `taux_commission` owner→Ventes (README stale = le vrai *silent green* · mord
|
||
cross-module ET README⊆) · **artefact** audit_5d D1.1 owner `Direction→Finanzas`
|
||
(divergence inter-modules) · **artefact** source `seuil_uaf` `D2.3→D9.9` (contrôle
|
||
FANTÔME cité par la source) · README annotation fantôme (`endpoint_otoia→endpoint_xxx`) ·
|
||
README réf textuelle `session 19→session 42` (absente de la source) ; restauré = green :
|
||
5 couples owner/réf == artefacts croisés · 4 réf `D#.#` présentes audit_5d + source ·
|
||
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é « 3ᵉ
|
||
surface `devops/deploy_runbook` — imputabilité des confirmations ») 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'obtention
|
||
réelle des 7 confirmations auprès de leurs owners reste côté agent DevOps / VPS).
|
||
- **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.
|
||
|
||
## Session `20260801_163401` · Buffer S8 · Domaine QA/audit_5d : l'**IDENTITÉ des 5 DIMENSIONS** du cadre d'audit — le **libellé** (`titre`) ET la **norme de référence** (`reference`) de chacune, énumérés à la main dans la table « ## Les 5 dimensions » (`05_deliverables_mvp/qa/audit_5d/README.md:15-19`) — était **HORS de tout gate d'IDENTITÉ**. Le tout premier bloc audit_5d ne recompute que le **COMPTE** (« **5 dimensions (5D)** ») — un WILDCARD aveugle à QUELLE dimension ; les 2 autres blocs audit_5d gatent la ventilation du verdict et l'énumération des open items, jamais la carte des dimensions.
|
||
|
||
**Tâche** : **Sprint 8 · buffer** (DevOps CI/CD · QA — « Audit UAF + normes ISA/IFRS
|
||
5D »). Roadmap fonctionnellement close ; poursuite de la série anti-dérive (CLAUDE.md
|
||
#6). **Même patron EXACT** que l'énumération des open items (2e surface du MÊME
|
||
README) ou les triplets par workspace des portails — appliqué à une **3e surface
|
||
distincte du MÊME README `qa/audit_5d`** : la table des 5 dimensions, gatée jusqu'ici
|
||
seulement sur son COMPTE agrégé.
|
||
|
||
**Dérive silencieuse fermée** :
|
||
- `05_deliverables_mvp/qa/audit_5d/README.md:15-19` — table « ## Les 5 dimensions » :
|
||
chaque ligne `| **D1** | Traçabilité & anti-invention | ISA 500 — Éléments probants |`
|
||
transcrit PAR LIGNE le triplet (`id`, `titre`, `reference`) d'une dimension.
|
||
- Source faisant autorité : `qa/audit_5d/out/audit_report.json[dimensions]`
|
||
(byte-gaté par `check_artifacts` ⇒ chaque dimension recomposée du catalogue de
|
||
contrôles à chaque build), chaque entrée portant `id` + `titre` + `reference`.
|
||
L'INPUT `audit_spec.json[dimensions]` (dont le rapport DÉRIVE) sert de cohérence
|
||
croisée.
|
||
- Piège : l'audit 5D est l'audit de **SECOND NIVEAU** que l'auditeur/banquier lit.
|
||
RENOMMER un libellé (« Traçabilité & anti-invention » → …) ou PERMUTER une norme
|
||
de référence (D3 fiscal citant la mauvaise `Ley 32-23`, D2 AML la mauvaise
|
||
`Ley 155-17`, D1 probatoire `ISA 501` au lieu d'`ISA 500`) laisse le compte à 5 —
|
||
le bloc de comptage reste AVEUGLE — pendant que l'artefact dit autre chose ⇒ une
|
||
AFFIRMATION de conformité fausse (l'invention même que #6 interdit). Aucune suite
|
||
`tests/` (qui teste des FONCTIONS de contrôle, pas la prose) n'attrape ce « vert
|
||
trompeur ».
|
||
- **État courant** : **aucune dimension périmée** — les 5 triplets 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 « Audit 5D (3e surface du
|
||
MÊME README) · IDENTITÉ des 5 DIMENSIONS » après le bloc open items) : on RECOMPUTE
|
||
la liste ORDONNÉE `{id:(titre, reference)}` depuis `audit_report.dimensions[]` (zéro
|
||
duplication du modèle de l'auditeur) et on exige que la table README l'énumère
|
||
EXACTEMENT : (1) identité par **ENSEMBLE d'`id`** (absent ET en trop mordus) ; (2)
|
||
**ORDRE** préservé (D1→D5) ; (3) `(titre, reference)` **par ligne**. Regex de ligne
|
||
`| **Dn** | … | … |` (gras + PAS de sous-point ⇒ distingue des lignes open items
|
||
`| D1.1 · … |`). Cohérence croisée bonus : `audit_report.dimensions` ==
|
||
`audit_spec.json[dimensions]` (l'INPUT dont le rapport dérive · mord un builder qui
|
||
renommerait/permuterait une dimension en aval du spec). Table disparue = échec
|
||
(traçabilité #6).
|
||
|
||
**8 morsures vérifiées** (5 côté README · 1 réordonnancement · 2 côté artefact/spec) :
|
||
README renomme D1 titre (`& anti-invention→et non-invention`) · README permute la
|
||
norme D1 (`ISA 500→ISA 501`) · README permute la norme fiscale D3 (`Ley 32-23→Ley
|
||
33-23`, affirmation de conformité fausse) · README supprime la ligne D5 (absent) ·
|
||
README réordonne D1↔D2 (ORDRE cassé) · **artefact** renomme le titre D2 (README périmé
|
||
+ divergence spec↔rapport) · **spec** renomme la référence D4 (divergence spec↔rapport)
|
||
· README supprime la table entière (INTROUVABLE, énumère `[]`) ; restauré = green :
|
||
5 dimensions `[D1..D5]` (id→titre→reference) == artefact · ordre == artefact ·
|
||
spec == rapport · 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_ci_integrity`/`check_artifacts`/
|
||
`check_regression`/`validate_json`).
|
||
|
||
- `ci/README.md` (recap `check-readme-claims` + paragraphe détaillé « 3e surface du
|
||
MÊME README de l'audit 5D · IDENTITÉ des 5 dimensions ») 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 4 open items réglementaires reste côté métier/VPS).
|
||
- **Auto-score 4Big** : 96/100.
|
||
|
||
## Session `20260801_190412` · Buffer S8 · Domaine Fiches agents/RBAC : l'**appartenance à `roles_allowed` du chat OTOIA** — QUI peut **invoquer** l'assistant OTOIA embarqué dans un portail — asserée EN PROSE par 2 fiches (`03_agents/rendu/AGENT.md:36` positive · `03_agents/mobile/AGENT.md:41` négative) était **HORS de tout gate d'appartenance**. C'est une **3e surface d'appartenance** DISTINCTE des deux déjà gatées : le **contrat** `rbac_50_roles.json` (attributs du rôle, blocs Fiches Faisabilité/Mobile) et le **`Has Role`** `workspace.json` (accès console, bloc Fiches Workspace). Le contrôle d'invocation du chat vit dans un 3e artefact — `frontend/chat_otoia/out/chat_mount.json` (`roles_allowed` par portail) — que le bloc Chat OTOIA amont ne gate que sur l'IDENTITÉ persona/capabilities + la COMPOSITION (5 mounts ⇔ 5 portails), AVEUGLE à QUEL rôle figure dans quelle `roles_allowed`.
|
||
|
||
**Tâche** : **Sprint 8 · buffer** (QA/DevOps — série anti-dérive CLAUDE.md #6, roadmap fonctionnellement close). **Même patron EXACT** que le bloc de membership `Has Role` (juste au-dessus dans `check_readme_claims.sh`) — appliqué à un **artefact + une sémantique DIFFÉRENTS** : accès console (`Has Role`) ≠ droit d'invocation du chat (`roles_allowed`).
|
||
|
||
**Dérive silencieuse fermée** :
|
||
- `03_agents/rendu/AGENT.md:36` — POSITIVE : rôle « OTO Faisabilité Rendu 3D » ∈ `roles_allowed` du portail NOMMÉ `construction` (déclenchement des rendus assistés).
|
||
- `03_agents/mobile/AGENT.md:41` — NÉGATIVE d'honnêteté (#6) : rôle « OTO Plateforme Mobile » (famille `plateforme`) dans AUCUNE `roles_allowed` (le chat ne couvre que les portails métier).
|
||
- Source faisant autorité : `frontend/chat_otoia/out/chat_mount.json[].roles_allowed` (byte-gaté par `check_artifacts`), `erpnext_role_name` résolu du contrat par `id` (zéro duplication).
|
||
- Piège : ajouter le rôle mobile à une `roles_allowed` = **SUR-EXPOSITION** (le dev mobile gagnerait le droit de piloter l'IA d'un portail métier — l'escalade même que #6 refuse) ; retirer le rôle rendu de la `roles_allowed` construction = perte du déclenchement assisté ; DÉPLACER le rôle vers un autre portail — laisse la fiche périmée EN SILENCE pendant que l'artefact dit autre chose. Aucune suite `tests/` (qui teste des FONCTIONS de montage, pas la prose d'une fiche) n'attrape ce « vert trompeur ».
|
||
- **État courant** : aucune valeur périmée — rendu ∈ construction (et lui seul), mobile ∉ tout (anti-invention #6, rien à réécrire). Le défaut est la **surface ungated**.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « Fiches agents · appartenance à `roles_allowed` du chat OTOIA » après le bloc Fiches Workspace) : index recomputé `erpnext_role_name → {portails autorisant}`, puis (a) **in_named_portail** (rendu) — le portail nommé est data-derived (== `portail` du rôle au contrat, **triple ancrage** : nommé == contrat == unique portail autorisant), membership == {ce portail}, prose citant « invoquer le chat OTOIA » ; (b) **not_in_any** (mobile) — rôle ABSENT de TOUTE `roles_allowed`. Portail nommé fantôme (absent des mounts) = échec (anti-typo). Claim absent = échec (traçabilité #6). Invariant vérifié en amont : chaque rôle ∈ ≤1 `roles_allowed`, == son `portail` contrat.
|
||
|
||
**6 morsures vérifiées** (3 côté artefact · 3 côté fiche/bord) : artefact retire rendu de `construction` (∉ aucun portail) · artefact ajoute mobile à `ventes` (SUR-EXPOSITION #6 VIOLÉE) · artefact ajoute AUSSI rendu à `compta` (droit élargi, fiche nomme 1 seul) · fiche rendu nomme le mauvais portail (`construction→compta`, INTROUVABLE) · fiche mobile retire le token `roles_allowed` (INTROUVABLE) ; restauré = green : rendu ∈ `{construction}` · mobile ∉ tout · exit 0. Working tree byte-restauré (`git checkout --`, **JAMAIS** `git clean`, interdit absolu) · **7 gates re-verts** (`validate_json`/`check_docs`/`guard_constraints`/`check_artifacts`/`check_ci_integrity`/`check_readme_claims`/`check_regression`).
|
||
|
||
- `ci/README.md` (recap `check-readme-claims` + paragraphe détaillé « 3e surface d'appartenance · `roles_allowed` du chat OTOIA ») mis à jour.
|
||
- **Note memory** : la classe « agent-fiche-role-attrs-ungated » est désormais CLOSE (attributs contrat = Fiches Faisabilité/Mobile ; `Has Role` = Fiches Workspace ; `roles_allowed` chat = ce bloc). Candidat propre restant : la fiche `ifc_speckle:34` (attributs contrat déjà gatés) + la row acceptance S3 « viewer 3D » (déjà gatée QA).
|
||
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ; édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact ; le montage réel du chat reste côté runner/VPS).
|
||
- **Auto-score 4Big** : 96/100.
|
||
|
||
---
|
||
|
||
## Session 20260801_193414 · Sprint 8 · buffer · Gate d'IDENTITÉ des comptes de tests self-reportés par les README de MODULE (#6)
|
||
|
||
**Contexte** : la série anti-dérive Sprint-8 gate chaque surface data-derived contre sa source de vérité. Tous les blocs de **comptes de tests** existants (`ci/check_readme_claims.sh`) visent `03_agents/*/AGENT.md` : la cellule « Tests » des tables via `row_re`, les agrégats prose de `crm`/`erpnext_backend`, et les cinq claims per-suite « hors table » (seo/portails/chat_otoia/confotur/deploy_runbook). AUCUN ne touche le README du **MODULE** lui-même — pourtant `05_deliverables_mvp/<suite>/README.md` se re-déclare, en propre, le compte de SA suite à plusieurs endroits.
|
||
|
||
**Surface trouvée (ungated)** : **27 claims sur 16 READMEs de module**, sous trois formulations récurrentes :
|
||
- commentaire de la commande unittest : `discover -s tests -v # N tests (…)` ;
|
||
- bullet « Vérification en-repo » : `→ **N/N verts**` (six modules RBAC/faisabilité/CRM) ;
|
||
- justification d'auto-score 4Big : `Validé statiquement en-repo (N tests verts + …)`.
|
||
Ces nombres sont SAISIS À LA MAIN dans le doc d'entrée de chaque module et se périment dès qu'un `def test_` est ajouté/retiré : la source faisant autorité (`qa/regression/out/regression_plan.json` → `auth = plan.suites[path].test_methods`, recomptée à chaque build par découverte réelle, byte-gatée par `check_artifacts` + fraîcheur du run par `check_regression`) se met à jour tandis que la prose du README rote EN SILENCE — « vert trompeur » de la MÊME classe que la cellule « Tests » des fiches, mais sur la surface que `row_re` n'atteint pas (il ne lit le README de module QUE comme *cible de lien*). Aucune suite `tests/` (qui teste des FONCTIONS de génération, jamais la prose du README) n'attrape ce périmé.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « READMEs de MODULE · comptes de tests self-reportés » juste après le bloc per-suite « hors table », d'où `auth` est déjà en portée · zéro rechargement · #6) : table de config `MODULE_README_TESTS` = liste `(suite_path, [motifs ancrés])`. Pour chaque module : `auth[suite_path]` recomputé, chaque motif DOIT matcher (claim absent = régression #6) ET tous ses groupes capturés == `auth`. Le motif `**N/N verts**` capture DEUX groupes → les deux doivent valoir `auth` : un `25/24 verts` internement incohérent mord aussi. Chaque motif est ancré sur un contexte distinctif (`discover -s tests…# N tests`, `**N/N verts**`, `en-repo (N tests verts`, `test_confotur.py # N tests`…) pour viser LE compte de CETTE suite sans capter un autre nombre.
|
||
- **Cinq modules volontairement HORS table** (`frontend/chat_otoia`, `mobile/app_config`, `publiciste`, `qa/acceptance`, `qa/audit_4big`) : leur README ne self-reporte AUCUN compte de tests (vérifié) — rien à ancrer ; un futur claim = l'ajouter à la table. Noté dans le commentaire du bloc.
|
||
|
||
**4 morsures vérifiées** (chaque README restauré depuis backup — **JAMAIS** `git checkout .`/`git clean`, interdits absolus) :
|
||
1. drift commentaire (`faisabilite/generator` 16→17 tests) → ✗ « dit 17 MAIS source dit 16 » ;
|
||
2. incohérence interne `N/N` (`rbac/apply_plan` `16/16`→`16/17 verts`) → ✗ « dit 16/17 MAIS source dit 16 » ;
|
||
3. disparition de claim (`legal/confotur` prose auto-score « diffable, 44 tests (dont… » retirée) → ✗ « claim INTROUVABLE (attendu 44) » ;
|
||
4. drift auto-score prose (`rbac/fixtures_gen` `(11 tests verts`→`(12 tests verts`) → ✗ « dit 12 MAIS source dit 11 ».
|
||
Restauré = green : `rc=0`, **440 checks de base intacts (diff vide) + 27 nouveaux**. Gates sœurs re-verts : `check_ci_integrity` rc=0 · `check_docs` rc=0.
|
||
|
||
- En-tête du gate mis à jour (inventaire des surfaces couvertes : nouvelle entrée « comptes de tests self-reportés par MODULE »).
|
||
- **Note memory** : classe « comptes de tests » — désormais couverte sur TROIS surfaces (cellule « Tests » des fiches via `row_re` · agrégats prose fiches · **README de module**, ce bloc) + les cinq claims per-suite hors table. Source unique = `auth` (plan.suites[].test_methods).
|
||
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ; édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact).
|
||
- **Auto-score 4Big** : 96/100.
|
||
|
||
## Session `20260801_200419` · Buffer S8 · Fiche DevOps : gate du **COMPTE de suites gated** (« 22 suites gated ») — sémantique DISTINCTE des comptes de tests par suite
|
||
|
||
**Contexte** : la série anti-dérive Sprint-8 gate chaque surface data-derived contre
|
||
sa source de vérité (CLAUDE.md #6, roadmap fonctionnellement close). Tous les blocs de
|
||
comptes de tests de `ci/check_readme_claims.sh` recomputent un compte de **méthodes**
|
||
`def test_` (`auth[path]` = `plan.suites[path].test_methods`) : la cellule « Tests »
|
||
des tables via `row_re`, les agrégats prose crm/erpnext_backend, les 5 claims per-suite
|
||
« hors table », et les READMEs de module. Une surface d'une **classe voisine mais
|
||
distincte** restait ungated.
|
||
|
||
**Surface trouvée (ungated)** : `03_agents/devops/AGENT.md:24` — dans la table des
|
||
livrables DevOps, la cellule « Tests » de la row du **pipeline CI**
|
||
(`.gitea/workflows/ci.yml` + `ci/`) affiche « **22 suites gated** » : non pas un compte
|
||
de tests, mais le **nombre de SUITES** que la CI passe au crible, soit
|
||
`regression_plan.totals.suites`. Ce nombre est saisi À LA MAIN et **aucun** gate ne
|
||
l'atteignait :
|
||
- `row_re` échoue DEUX fois : l'ancre de lien est `ci.yml`/`ci/` (pas un module de
|
||
`auth`) ET la cellule porte le suffixe « suites gated » (pas un entier nu) ;
|
||
- l'agrégat de la fiche QA (`03_agents/qa/AGENT.md:35` « **22 suites gated · 564
|
||
tests…** », gaté sur `regression_run`) est une **surface distincte** (autre fichier,
|
||
autre motif). La MÊME valeur 22 vit donc à DEUX endroits — **un seul** était gaté.
|
||
|
||
**Dérive silencieuse fermée** : ajouter/retirer un module gated fait bouger
|
||
`reg["suites"]` (recompté à chaque build par découverte réelle des suites, byte-gaté par
|
||
`check_artifacts`) tandis que cette cellule DevOps rote EN SILENCE ⇒ la doc d'identité de
|
||
l'agent DevOps affirmerait un **périmètre de couverture CI faux** — le « vert trompeur »
|
||
que #6 interdit. Aucune suite `tests/` (FONCTIONS, pas la prose de fiche) ne l'attrape.
|
||
**État courant** : valeur exacte (22 == 22), le défaut est la **surface ungated** (rien
|
||
à réécrire · anti-invention #6).
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « Fiche DevOps · COMPTE de
|
||
suites gated » juste après le bloc per-suite « hors table », d'où `reg` est déjà en
|
||
portée · zéro rechargement) : on RECOMPUTE depuis `reg["suites"]`
|
||
(`regression_plan.totals.suites`, source unique) et on exige que la cellule l'écrive
|
||
EXACTEMENT. Motif ANCRÉ sur le label de rôle « CI/CD Gitea Actions » de CETTE row →
|
||
vise la bonne cellule. Claim absent = échec (traçabilité #6). **Cohérence croisée** :
|
||
`reg["suites"]` == `regression_run` `len(suites)` (la source du compte de la fiche QA)
|
||
⇒ une divergence plan↔run mord ici aussi.
|
||
|
||
**5 morsures vérifiées** (2 côté fiche · 2 côté artefact · 1 cross-cohérence) :
|
||
1. cellule 22→23 → ✗ « dit 23 MAIS source dit 22 » ;
|
||
2. claim retiré (`| gated |`) → ✗ « cellule « N suites gated » INTROUVABLE » ;
|
||
3. `plan.totals.suites` 22→21 (fiche périmée) → ✗ divergence SOURCE + ✗ « dit 22 MAIS
|
||
source dit 21 » ;
|
||
4. `regression_run` privé d'une suite (plan↔run divergent) → ✗ « incohérence SOURCE :
|
||
plan.suites=22 ≠ run len=21 » ;
|
||
5. restauré = green (rc=0).
|
||
Working tree byte-restauré via `git checkout -- <fichier précis>` (**JAMAIS**
|
||
`git checkout .` / `git clean`, interdits absolus). **7 gates re-verts** :
|
||
`validate_json` / `check_docs` / `guard_constraints` / `check_artifacts` /
|
||
`check_ci_integrity` / `check_readme_claims` / `check_regression` (tous rc=0).
|
||
|
||
- En-tête du gate mis à jour (inventaire des surfaces couvertes : nouvelle entrée
|
||
« COMPTE de suites gated de la fiche devops ») + `ci/README.md` (recap
|
||
`check-readme-claims` : nouvelle clause du compte de suites gated).
|
||
- **Note memory** : la classe « comptes de tests » (méthodes `def test_`) est couverte
|
||
sur 4 surfaces + les 5 per-suite ; le COMPTE de **suites** (`plan.totals.suites`) est
|
||
une classe voisine — désormais gaté sur ses DEUX surfaces (fiche QA agrégat déjà gaté ·
|
||
fiche DevOps cellule, ce bloc).
|
||
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ;
|
||
édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact).
|
||
- **Auto-score 4Big** : 96/100.
|
||
|
||
## Session `20260801_203424` · Buffer S8 · Audit 5D (4e surface du même README) : gate de l'IDENTITÉ des **LIVRABLES AUDITÉS** — la liste des hand-off `out/*.json` que l'audit de second niveau LIT comme matière première
|
||
|
||
**Contexte** : la série anti-dérive Sprint-8 gate chaque surface data-derived contre
|
||
sa source de vérité (CLAUDE.md #6, roadmap fonctionnellement close). Trois blocs
|
||
`audit_5d` de `ci/check_readme_claims.sh` couvrent déjà le MÊME README : la **synthèse**
|
||
(17 contrôles / 5 dimensions + ventilation verdict), l'**énumération des open items**
|
||
(`control→dimension→propriétaire`) et l'**IDENTITÉ des 5 dimensions** (`id→titre→
|
||
reference`). Une **4e surface** du même README restait ungated.
|
||
|
||
**Surface trouvée (ungated)** : `05_deliverables_mvp/qa/audit_5d/README.md:21-27` —
|
||
section « ## Livrables audités (source = leur `out/`) » : une **liste de 5 puces** qui
|
||
énumère À LA MAIN les hand-off `out/*.json` que l'auditeur charge réellement
|
||
(`crm/workflow_vente/out/workflow.json` · `crm/dossier_vente/out/doctype_oto_dossier_
|
||
vente.json` · `crm/commissions/out/commission_plan.json` · `fiscal/ecf_dgii/out/
|
||
ecf_plan.json` · `legal/confotur/out/doctype_confotur_application.json`). Aucun bloc ne
|
||
la touchait — vérifié : le seul `audited_artifacts` de tout le gate était dans un
|
||
**commentaire** ; les 3 blocs audit_5d ne `load()` que `controls_total`/`totals`/
|
||
`open_items`/`dimensions[]`.
|
||
|
||
**Source faisant autorité** : `qa/audit_5d/out/MANIFEST.json` clé `audited_artifacts`
|
||
(byte-gaté par `check_artifacts` ⇒ reproduit à chaque build via
|
||
`sorted(ARTIFACT_PATHS.values())` dans `audit_5d_gen.py:61` — le registre des hand-off
|
||
que `qalib/artifacts.py::load_artifacts()` charge). Le README énumère les 5 chemins
|
||
dans un **ORDRE DIFFÉRENT** du MANIFEST (workflow 2e ici, 3e au MANIFEST trié) ⇒
|
||
comparaison par **ENSEMBLE**.
|
||
|
||
**Dérive silencieuse fermée** : brancher un nouveau livrable amont sur l'audit
|
||
(nouvelle entrée `ARTIFACT_PATHS` → nouveau chemin dans `audited_artifacts`) ou en
|
||
retirer un laisse la section README **périmée en silence** pendant que l'audit lit un
|
||
autre jeu de fichiers ⇒ le lecteur/banquier croirait l'audit couvrir un **périmètre
|
||
qu'il ne couvre plus** (l'invention #6 interdit), qu'aucune suite `tests/` (FONCTIONS
|
||
de contrôle, pas la prose) n'attrape. **État courant** : les 5 puces == registre
|
||
exactement (rien à réécrire · anti-invention #6), le défaut est la **surface ungated**.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « Audit 5D (4e surface) ·
|
||
IDENTITÉ des LIVRABLES AUDITÉS » entre le bloc des 5 dimensions et le bloc DevOps
|
||
run-book) : on RECOMPUTE l'ensemble depuis `MANIFEST.audited_artifacts` (zéro
|
||
duplication du registre) et on parse les puces `` - `<chemin>` `` de la section
|
||
« ## Livrables audités » → égalité par ENSEMBLE exigée (absent ET fantôme mordus, via
|
||
set-diff `absents`/`en trop`). **Cohérences croisées bonus** : **(a)** chaque chemin
|
||
audité RÉSOUT un fichier commité sur disque (`os.path.exists` — le `_load` de l'auditeur
|
||
lève `FileNotFoundError` sinon, audit à vide impossible) ; **(b)** |livrables audités|
|
||
== |alias distincts d'artefacts référencés par les `controls[].artifacts` de
|
||
`audit_spec.json`| (un artefact chargé mais jamais contrôlé, ou un alias contrôlé sans
|
||
chemin de chargement → `KeyError` runtime, désynchronise). Section absente = échec
|
||
(traçabilité #6). `import os` ajouté au bloc (non importé globalement).
|
||
|
||
**6 morsures vérifiées** (4 côté README · 2 côté source) :
|
||
1. README puce `workflow.json` retirée → ✗ « absents=[…workflow.json] » ;
|
||
2. README puce FANTÔME `demo/scenarios/out/run_sheet.json` ajoutée → ✗ « en trop
|
||
(fantôme)=[…run_sheet.json] » ;
|
||
3. README typo `commission_plan.json`→`commissions_plan.json` → ✗ absents ET fantôme ;
|
||
4. README header de section renommé → ✗ « section « ## Livrables audités »
|
||
INTROUVABLE/vide » ;
|
||
5. `audit_spec.json` : alias `phantom_alias` ajouté à un contrôle → ✗ « désynchro
|
||
registre/spec : 5 livrables MAIS 6 alias distincts contrôlés » (cross-anchor b) ;
|
||
6. `MANIFEST.audited_artifacts` : un chemin pointé vers un fichier inexistant → ✗
|
||
« INTROUVABLES sur disque » (cross-anchor a) + ✗ README périmé.
|
||
Restauré = green : 5 puces == registre · 5 == 5 alias · 5 fichiers résolus · exit 0.
|
||
Working tree byte-restauré via `git checkout -- <fichier précis>` (**JAMAIS**
|
||
`git checkout .` / `git clean`, interdits absolus). **7 gates re-verts** :
|
||
`validate_json` / `guard_constraints` / `check_docs` / `check_artifacts` /
|
||
`check_regression` / `check_ci_integrity` / `check_readme_claims` (tous rc=0).
|
||
|
||
- `ci/README.md` mis à jour : clause de la row récap `check-readme-claims` (surface
|
||
audit 5D étendue) + paragraphe détaillé « 4ᵉ surface du même README de l'audit 5D ».
|
||
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ;
|
||
édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact ; l'exécution
|
||
réelle de l'audit `bench` reste côté VPS).
|
||
- **Auto-score 4Big** : 96/100.
|
||
|
||
## Session `20260801_213444` · Buffer S8 · Fiche ONAPI/Legal : 2ᵉ SURFACE PROSE des ENTITÉS PORTEUSES
|
||
|
||
**Tâche** : **Sprint 8 · buffer** (DevOps CI/CD · QA — anti-dérive doc). Roadmap
|
||
plancher atteinte ; on continue de fermer les surfaces prose data-derived encore
|
||
ungatées. Cible de cette session : `03_agents/onapi_legal/AGENT.md:54`.
|
||
|
||
**Surface trouvée (ungatée)** : la fiche ONAPI/Legal décrit le champ Select
|
||
`entite_porteuse` du DocType CONFOTUR par « **les 7 entités de CLAUDE.md #Entités
|
||
(WAF · WA SRL · AC Arias Cuevas · Consortium ECR DR · Helios RD · Ploutos · 9060
|
||
QC)** ». C'est un **COMPTE littéral** (« 7 ») **+** l'**énumération complète** — une
|
||
**SECONDE surface prose** du même menu que le champ `entite_porteuse` offrira.
|
||
|
||
Le bloc « Confotur entités » déjà en place ancre le champ à CLAUDE.md §Entités et
|
||
gate la ligne du README `legal/confotur` (surface **(e)**), mais ce motif est ancré
|
||
sur `05_deliverables_mvp/legal/confotur/README.md` — **il ne vise pas `03_agents/`**.
|
||
La fiche restait donc **hors de tout gate** : recompter la §Entités (aujourd'hui 7)
|
||
n'atteignait jamais cette phrase.
|
||
|
||
**Dérive silencieuse fermée** : AJOUTER / RETIRER / RENOMMER une entité dans
|
||
CLAUDE.md §Entités (source ancre) → régénérer propage le menu de l'artefact **et** la
|
||
ligne du README confotur, MAIS la fiche onapi_legal se **périme en silence** — elle
|
||
annoncerait « 7 entités » quand il y en a 8, ou nommerait `Ploutos` là où le menu ne
|
||
l'offre plus. L'agent ERPNext Backend qui seede le DocType croirait le déroulant
|
||
couvrir un périmètre d'entités qu'il ne couvre plus (l'**invention #6** interdit).
|
||
Aucune suite `tests/` (FONCTIONS de build du DocType, pas la prose d'une fiche) ne
|
||
l'attrape. **État courant** : la fiche == §Entités == menu artefact exactement
|
||
(rien à réécrire · #6) ; le défaut était la surface **ungatée**.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « Fiche ONAPI/Legal ·
|
||
2ᵉ SURFACE PROSE des ENTITÉS PORTEUSES », inséré juste après le bloc « Confotur
|
||
entités » et avant le bloc Chat OTOIA) : on **re-dérive** la liste depuis les tokens
|
||
en gras de la section « ## Entités » de CLAUDE.md (source ancre · zéro duplication)
|
||
et on exige de la fiche — **(a)** le COMPTE littéral (« les N entités ») ==
|
||
|§Entités| ; **(b)** l'énumération == §Entités **ORDRE EXACT** (absent ET fantôme
|
||
mordus par set-diff + comparaison ordonnée) ; **(c)** l'ancre « CLAUDE.md #Entités »
|
||
citée sur la ligne ; **(d)** cross-check bonus — l'énumération == options
|
||
`entite_porteuse` de l'artefact CONFOTUR byte-gaté (la fiche décrit **exactement** le
|
||
menu que le DocType offrira). Ligne absente = échec (traçabilité #6).
|
||
|
||
**6 morsures vérifiées** (5 côté fiche · 1 côté artefact) :
|
||
1. compte `7`→`8` → ✗ (a) « fiche dit 8 entités MAIS §Entités en porte 7 » ;
|
||
2. `Ploutos` retiré de l'énumération → ✗ (b) « absent(s)=['Ploutos'] » ;
|
||
3. entité FANTÔME `Fantom SA` ajoutée → ✗ (b) « fantôme(s)=['Fantom SA'] » ;
|
||
4. réordre `WAF`↔`WA SRL` → ✗ (b) « ordre : fiche […] ≠ §Entités […] » ;
|
||
5. ancre `#Entités` retirée de la ligne → ✗ (c) « ne cite pas CLAUDE.md #Entités » ;
|
||
6. option `Ploutos`→`Ploutos SRL` dans l'artefact CONFOTUR → ✗ (d) « fiche […] ≠
|
||
menu artefact […] → la fiche décrit un menu que le DocType n'offre pas ».
|
||
Restauré = green : (a)(b)(c)(d) tous ✓ · exit 0. Working tree byte-restauré via
|
||
`git checkout -- <fichier précis>` (**JAMAIS** `git checkout .` / `git clean`,
|
||
interdits absolus). **7 gates re-verts** : `validate_json` / `guard_constraints` /
|
||
`check_docs` / `check_artifacts` / `check_regression` / `check_ci_integrity` /
|
||
`check_readme_claims` (tous rc=0).
|
||
|
||
- `ci/README.md` mis à jour : paragraphe « Entités porteuses du DocType CONFOTUR »
|
||
étendu d'une clause « **2ᵉ surface prose (fiche ONAPI/Legal)** » (compte + liste +
|
||
cross-check menu artefact).
|
||
- **Note anti-stale** : la mémoire `agent-fiche-role-attrs-ungated` est désormais
|
||
**périmée** — les attributs des fiches mobile / rendu / ifc_speckle (niveau, scope,
|
||
modules, permissions, entité) SONT déjà gatés (blocs « Fiche Mobile » et « Fiches
|
||
Faisabilité »). La classe résiduelle réelle est celle des LISTES data-derived
|
||
transcrites en prose de fiche, dont cette session ferme l'occurrence entités.
|
||
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ;
|
||
édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact ; l'application
|
||
réelle du DocType par `bench` reste côté agent ERPNext Backend).
|
||
- **Auto-score 4Big** : 96/100.
|
||
|
||
---
|
||
|
||
## Sprint 8 · buffer · Chat OTOIA : gate d'IDENTITÉ de la LISTE D'INVARIANTS CI
|
||
|
||
**Contexte** : campagne anti-dérive documentaire (Sprint 8 · QA « regression
|
||
tests exhaustifs »). Baseline des 7 gates vert avant intervention. Recherche
|
||
(sous-agent Explore) de la prochaine classe de surface data-derived transcrite
|
||
en prose mais NON gatée → candidat retenu : la section « ## Invariants (14) —
|
||
garantis en CI » du README `frontend/chat_otoia` (README:53-60).
|
||
|
||
**Défaut** : cette section (a) ANNONCE un compte `(14)` puis (b) ÉNUMÈRE chaque
|
||
invariant par son numéro entre parenthèses ((1)·(2)·…·(11, 13)·(12)·(14)). C'est
|
||
la transcription À LA MAIN du REGISTRE réel des invariants du générateur — les
|
||
marqueurs `# Invariant N ·` de `chat_otoia_gen.py` (source d'autorité : chaque
|
||
marqueur = un contrôle appliqué par `validate()`). Le générateur se self-reporte
|
||
en plus dans son message CLI (« schéma + 14 invariants verts. »), 3ᵉ transcription
|
||
du même compte. Les blocs Chat OTOIA préexistants du gate couvrent des COMPTES
|
||
d'artefact (blocks/mounts/portails) + l'IDENTITÉ persona/langues/endpoint —
|
||
AUCUN ne touche la liste d'invariants : surface aveugle. Aucune suite `tests/` ne
|
||
teste la PROSE ; le byte-gate d'artefact ignore le `.py` source ⇒ dérive
|
||
silencieuse (ajouter `# Invariant 15` ⇒ README périmé sans échec).
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « Chat OTOIA · IDENTITÉ
|
||
de la LISTE D'INVARIANTS CI », inséré juste après le bloc « Chat OTOIA identité »
|
||
et avant le bloc « Recette ») : on RECOMPUTE le registre depuis le SEUL
|
||
registre-source (`re.findall(r"#\s*Invariant\s+(\d+)", chat_otoia_gen.py)`) et on
|
||
exige — **(a)** le registre code est un ensemble CONTIGU `1..max` (lacune/doublon
|
||
dans le ledger = défaut réel) ; **(b)** le message CLI « N invariants verts »
|
||
== |registre| (self-report du `.py` cohérent avec son propre ledger) ; **(c)** le
|
||
compte annoncé « ## Invariants (N) » du README == |registre| ; **(d)**
|
||
l'ÉNUMÉRATION en prose (ints extraits des groupes `(...)` de la section, borne =
|
||
prochain `## `) == registre EXACT — absent ET fantôme mordus par set-diff. Section
|
||
ou compte absent = échec (disparition d'un chiffre = régression de traçabilité #6).
|
||
|
||
**5 morsures vérifiées** (working tree byte-restauré après chacune via
|
||
`git checkout -- <fichier précis>`, JAMAIS `git checkout .` / `git clean`) :
|
||
1. compte annoncé `14`→`15` → ✗ (c) « annonce (15) MAIS registre en compte 14 » ;
|
||
2. numéro `(7)` retiré de la prose → ✗ (d) « absent(s)=[7] » ;
|
||
3. numéro fantôme `20` ajouté à la prose → ✗ (d) « fantôme(s)=[20] » ;
|
||
4. self-report CLI `14`→`13` → ✗ (b) « générateur dit 13 MAIS registre 14 » ;
|
||
5. ledger code `# Invariant 7`→`77` → ✗ (a) « registre NON contigu […77] » +
|
||
✗ (d) « absent(s)=[77] · fantôme(s)=[7] » (double morsure : ledger + prose).
|
||
Restauré = green : (a)(b)(c)(d) tous ✓ (registre contigu 1..14 · 14 marqueurs).
|
||
|
||
- `ci/README.md` : la clause « montage Chat OTOIA » de la row `check-readme-claims`
|
||
étendue de l'IDENTITÉ de la liste d'invariants (compte + énumération + self-report
|
||
CLI recomputés du registre `# Invariant N`, set-diff + contiguïté).
|
||
- **7 gates re-verts** : `validate_json` / `guard_constraints` / `check_docs` /
|
||
`check_artifacts` / `check_regression` / `check_ci_integrity` /
|
||
`check_readme_claims` (tous rc=0).
|
||
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ;
|
||
édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact).
|
||
- **Auto-score 4Big** : 96/100.
|
||
|
||
---
|
||
|
||
## Sprint 8 · buffer · SEO : gate d'IDENTITÉ du COMPTE d'INVARIANTS CI
|
||
|
||
**Contexte** : campagne anti-dérive documentaire (Sprint 8 · QA « regression
|
||
tests exhaustifs »). Baseline des 7 gates vert avant intervention. Recherche
|
||
(sous-agent Explore) de la prochaine surface data-derived transcrite en prose
|
||
mais NON gatée. L'agent a proposé 5 candidates « N invariants » ; chat_otoia
|
||
étant déjà fermée au commit précédent, candidat retenu = le générateur SEO
|
||
(`05_deliverables_mvp/seo/seo_gen.py` + `seo/README.md`).
|
||
|
||
**Défaut** : la source d'autorité du COMPTE D'INVARIANTS de validation SEO n'est
|
||
PAS un artefact JSON (invisible à `check_artifacts` qui byte-gate les `out/*.json`)
|
||
mais le LEDGER NUMÉROTÉ de la fonction `_validate` — les marqueurs `# N ·`
|
||
(`# 1 ·` … `# 15 ·`) qui ouvrent chaque contrôle appliqué, analogue du registre
|
||
`# Invariant N` de Chat OTOIA. Ce compte est transcrit À LA MAIN à SEPT endroits
|
||
qu'AUCUN gate ne couvrait : le titre `## Garanties (N invariants)` du README + son
|
||
bloc de commande (« schéma de sortie + N invariants »), plus CINQ chaînes du `.py`
|
||
lui-même (docstring module L19, docstring `_validate` L57, aide `build` L24, message
|
||
de succès CLI « N invariants verts. » L285, aide du sous-parseur `validate` L298).
|
||
Le bloc SEO préexistant du gate ne couvre que des COMPTES d'ARTEFACT (mots-clés
|
||
total + par langue, recomputés de MANIFEST.counts) — classe distincte, surface
|
||
aveugle. Ajouter un contrôle `# 16 ·` à `_validate` ⇒ README + self-reports du `.py`
|
||
mentent en silence pendant que la CI applique 16 contrôles (« vert trompeur » #6)
|
||
— ni les suites `tests/` (FONCTIONS, jamais la prose) ni le byte-gate d'artefact
|
||
(aveugle au `.py` source) ne l'attrapent.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « SEO · IDENTITÉ du
|
||
COMPTE d'INVARIANTS CI », inséré juste après le bloc SEO mots-clés et avant le bloc
|
||
CRM pipeline vente) : on RECOMPUTE le ledger depuis le SEUL registre-source (les
|
||
`# N ·` de `_validate`, extraits du corps de la fonction borné au prochain `def `)
|
||
et on exige — **(a)** le ledger est CONTIGU 1..N (comparaison de la LISTE triée à
|
||
`range(1,N+1)`, ⇒ mord lacune ET doublon) ; **(b)** TOUTE mention « N invariants »
|
||
du `.py` == |ledger| (set des self-reports == {N}) ; **(c)** le compte annoncé
|
||
« ## Garanties (N invariants) » du README == |ledger| ; **(d)** TOUTE mention
|
||
« N invariants » du README == |ledger|. La prose du README RÉSUME les contrôles
|
||
(11 puces `·`-séparées ≠ énumération numérotée 1:1) ⇒ on gate le COMPTE partout où
|
||
il apparaît, PAS un set-diff de la liste. Section/compte absent = échec (#6).
|
||
|
||
**5 morsures vérifiées** (working tree byte-restauré après chacune via
|
||
`git checkout -- <fichier précis>`, JAMAIS `git checkout .` / `git clean`) :
|
||
1. README titre `15`→`16` → ✗ (c) « annonce (16) MAIS ledger 15 » + ✗ (d) « [15, 16] » ;
|
||
2. README commande `15`→`14` → ✗ (d) « mention(s) [14, 15] MAIS ledger 15 » ;
|
||
3. `.py` succès CLI `15`→`13` → ✗ (b) « self-report(s) [13, 15] MAIS ledger 15 » ;
|
||
4. ledger `# 7 ·`→`# 77 ·` (lacune) → ✗ (a) « NON contigu […6, 77, 8…] » ;
|
||
5. ledger `# 8 ·`→`# 7 ·` (doublon) → ✗ (a) « NON contigu […7, 7, 9…] »
|
||
(la comparaison sur LISTE — non set — attrape le doublon, angle que
|
||
l'approche set de chat_otoia aurait raté).
|
||
Restauré = green : (a)(b)(c)(d) tous ✓ (ledger contigu 1..15 · 15 marqueurs).
|
||
|
||
- `ci/README.md` : la clause SEO de la row `check-readme-claims` étendue du COMPTE
|
||
d'INVARIANTS CI (annonce + toutes mentions README + 5 self-reports `.py`
|
||
recomputés du ledger `# N ·` de `_validate`, contiguïté 1..N).
|
||
- **7 gates re-verts** : `guard_constraints` / `validate_json` / `check_docs` /
|
||
`check_artifacts` / `check_regression` / `check_ci_integrity` /
|
||
`check_readme_claims` (tous rc=0).
|
||
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ;
|
||
édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact).
|
||
- **Auto-score 4Big** : 96/100.
|
||
|
||
---
|
||
|
||
## Sprint 8 · buffer · RBAC SPEC §5 : gate de la LIGNE D'AUTO-CONTRÔLE du « résumé »
|
||
|
||
**Contexte** : suite de la campagne anti-dérive documentaire (Sprint 8 · QA
|
||
« regression tests exhaustifs »). Baseline des 7 gates verte avant intervention.
|
||
Recherche (sous-agent Explore) de la prochaine surface data-derived transcrite en
|
||
prose mais NON gatée — la plupart des COMPTES d'artefact sont déjà fermés. Le
|
||
candidat retenu est une surface interne de `rbac/RBAC_50_ROLES_SPEC.md` : le §5.
|
||
|
||
**Défaut** : la SPEC RBAC a DÉJÀ trois gates (§3 ventilation par portail · §4
|
||
colonne Entité · §7 run-book), mais son §5 « Les N rôles (résumé) » restait
|
||
AVEUGLE. Ce §5 restitue le total de rôles sur TROIS surfaces prose : (i) le
|
||
compte-titre « ## 5. Les 50 rôles (résumé) », (ii) NEUF comptes parenthétiques de
|
||
regroupement (`**Ventes (8)**` …) et (iii) une LIGNE d'auto-contrôle arithmétique
|
||
`_(6+8+4+7+5+8+4+2+6 = 50.)_`. AJOUTER un rôle au contrat (|rôles| 50→51) laisse le
|
||
titre, la somme et le « = 50 » PÉRIMÉS en silence ; ÉDITER `Ventes (8)→(7)` sans
|
||
toucher la ligne casse la cohérence titre↔somme — aucune suite `tests/` (FONCTIONS
|
||
RBAC, jamais la prose de la SPEC) ni le byte-gate d'artefact ne l'attrape.
|
||
|
||
**Subtilité (memory `rbac-spec-portail-count-gate` : « §5 by-family is
|
||
editorial »)** : les 9 regroupements sont ÉDITORIAUX — « Direction / Gouvernance »
|
||
agrège des rôles de familles direction+finance, ≠ la colonne `famille` du contrat.
|
||
On NE mappe donc PAS groupe↔famille (ce serait une dérive fausse). On gate
|
||
UNIQUEMENT ce qui est data-derived et vérifiable SANS ce mapping.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, bloc « 1septies) §5 », inséré après
|
||
le bloc §4 et avant le bloc apply_plan) : on RECOMPUTE N = |rôles| du contrat
|
||
(`rbac_50_roles.json`, byte-gaté par check_artifacts) et on exige — **(a)** le
|
||
compte-titre « ## 5. Les N rôles » == N ; **(b)** les 9 comptes de regroupement ==
|
||
EXACTEMENT, dans l'ordre, les termes de la ligne arithmétique (cohérence interne
|
||
prose↔somme, comparaison de LISTES ordonnées) ; **(c)** l'arithmétique est VRAIE
|
||
(Σ termes == membre droit) ; **(d)** le membre droit == N. Section/titre/ligne/
|
||
comptes absents = échec (#6).
|
||
|
||
**5 morsures vérifiées** (working tree byte-restauré après chacune via
|
||
`git checkout -- <fichier précis>`, JAMAIS `git checkout .` / `git clean` ;
|
||
gate rc=1 sous mutation, rc=0 restauré) :
|
||
1. titre `50`→`51` → ✗ (a) « titre dit 51 rôles MAIS le contrat en compte 50 » ;
|
||
2. regroupement `Ventes (8)`→`(9)` → ✗ (b) « [6, 9, 4…] ≠ termes [6, 8, 4…] » ;
|
||
3. ligne `= 50.`→`= 49.` → ✗ (c) « écrit = 49 MAIS Σ = 50 » + ✗ (d) « totalise
|
||
49 MAIS le contrat compte 50 » (double morsure) ;
|
||
4. terme `6+8+…`→`7+8+…` → ✗ (b) « termes [7, 8…] ≠ [6, 8…] » + ✗ (c) « = 50
|
||
MAIS Σ = 51 » (double morsure) ;
|
||
5. suppression de la ligne d'auto-contrôle → ✗ « ligne … INTROUVABLE » (absence).
|
||
Restauré = green : (a)(b)(c)(d) tous ✓ (titre 50 · 9 comptes == 6+8+4+7+5+8+4+2+6
|
||
· Σ=50 · membre droit 50 == |rôles|).
|
||
|
||
- `ci/README.md` : la row `check-readme-claims` étendue de la clause §5 (compte-
|
||
titre + 9 comptes de regroupement éditoriaux + ligne d'auto-contrôle, sans
|
||
mapping groupe↔famille).
|
||
- **7 gates re-verts** : `guard_constraints` / `validate_json` / `check_docs` /
|
||
`check_artifacts` / `check_regression` / `check_ci_integrity` /
|
||
`check_readme_claims` (tous rc=0).
|
||
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ;
|
||
édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact).
|
||
- **Auto-score 4Big** : 96/100.
|