ccb86fe7ca
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
887 lines
59 KiB
Markdown
887 lines
59 KiB
Markdown
# Activity log · 2026-08-02 · Claude Code DTP Worker
|
||
|
||
## Sprint 8 · buffer · Ledger d'invariants — 2ᵉ FAMILLE (`# INVn ·` / `validate_bundle` / « N familles d'invariants ») : ancrage du COMPTE de `deploy_runbook` (11) + `acceptance` (11)
|
||
|
||
**Contexte** — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention de
|
||
chiffres »). La classe « ledger d'invariants » (recompute le nombre de contrôles CI
|
||
depuis le registre-source des marqueurs numérotés de la fonction `_validate`, jamais
|
||
une liste à la main) avait été **déclarée close à 8 générateurs** — mais sur le SEUL
|
||
dialecte `# N ·` / « N invariants » / `_validate`(`_validate_bundle`). La mémoire
|
||
`invariant-ledger-count-gate` consigne précisément le risque : « un prior *CLOSED
|
||
après 7* était FAUX — un style de marqueur différent avait glissé ; re-scanner LES
|
||
DEUX styles avant de croire la clôture ». Un **re-scan systématique** a exhumé une
|
||
**DEUXIÈME famille** entièrement HORS gate : marqueurs `# INVn ·` (préfixe `INV`)
|
||
dans `validate_bundle`, phrasé « N **familles d'**invariants ». DEUX générateurs la
|
||
portent :
|
||
- `devops/deploy_runbook/deploy_runbook_gen.py` — ledger `# INV1..INV11` dans
|
||
`validate_bundle`, compte **« 11 familles d'invariants »** recopié à **3 surfaces
|
||
vives** : générateur l.272 (message CLI de succès) · README l.73 (bloc de commande)
|
||
· suite tests l.4 (docstring) ;
|
||
- `qa/acceptance/acceptance_gen.py` — ledger `# INV1..INV11` dans `validate_bundle`,
|
||
compte à **2 surfaces vives** : README l.46 « 11 invariants » · suite tests l.4
|
||
« 11 familles d'invariants ». (Le `.py` acceptance ne porte AUCUN compte numéroté
|
||
→ surface non exigée.)
|
||
|
||
**Le « vert trompeur »** — ajouter un contrôle `# INV12 ·` à `validate_bundle` sans
|
||
toucher ces chaînes ⇒ README + self-reports + docstrings de test mentent en silence
|
||
pendant que la CI applique 12 contrôles. Ni `check_artifacts` (le `.py` source n'est
|
||
pas un `out/*.json`) ni les suites `tests/` (FONCTIONS de validation, jamais la prose)
|
||
n'attrapent la dérive. **Prouvé (M4 SILENT-GREEN)** : injecter `# INV12 ·` dans
|
||
`validate_bundle` (ledger→12, toute la prose reste 11) ⇒ les **3 surfaces
|
||
deploy_runbook mordent** « [11] MAIS ledger en compte 12 » — preuve que l'autorité
|
||
est le **CODE-registre**, pas la cohérence interne de la prose.
|
||
|
||
**Particularités** — Ledger PROPRE 1..N compté À PART du schéma (« schéma + N
|
||
invariants »). Les mentions NON numérotées de « invariants » (docstrings « invariants
|
||
de déploiement/recette », README « Un invariant refuse … ») ne sont pas ancrées par
|
||
un chiffre → **aucun faux positif**. Le compte-regex accepte les DEUX formes
|
||
(« N invariants » ET « N familles d'invariants ») via un préfixe optionnel
|
||
`(?:familles d')?`. Les `daily_reports`/`activity_log` (snapshots datés — ex.
|
||
acceptance « 10 » d'avant la croissance 10→11) ne sont PAS des surfaces vives et ne
|
||
sont volontairement pas ancrés (aucune dérive à surveiller sur un log figé).
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, bloc « DevOps deploy_runbook + QA
|
||
acceptance · IDENTITÉ du COMPTE d'INVARIANTS de `validate_bundle` », juste après le
|
||
bloc Mobile) — **paramétré sur les 2 modules** (zéro duplication). RECOMPUTE le
|
||
ledger depuis le SEUL registre-source (`# INVn ·` de `validate_bundle`, corps extrait
|
||
jusqu'au `def` suivant) et exige : **(a)** ledger CONTIGU 1..N (lacune OU doublon nu
|
||
= vrai défaut de registre) ; **(b)** CHAQUE surface déclarée porte ≥1 mention
|
||
« N [familles d']invariants » toutes == |ledger|. Une surface sans mention = self-report
|
||
ÉVAPORÉ (échec · anti-évaporation #6).
|
||
|
||
**Vérif** — 7 checks verts sur l'arbre propre (2 ledgers 1..11 + 5 surfaces).
|
||
**8 morsures** adversariales, gate lancé SEUL, reverts par `cp` ciblé (jamais
|
||
`git checkout` large — cf. incident consigné) : **M1** deploy `.py` 11→12 (b générateur)
|
||
· **M2** deploy README 11→12 (b) · **M3** deploy test 11→12 (b) · **M4 SILENT-GREEN**
|
||
`# INV12 ·` injecté ⇒ 3 surfaces deploy mordent (autorité = CODE) · **M5** `# INV7 ·`
|
||
supprimé ⇒ ledger non contigu (a) + 3 surfaces désync · **M6** acceptance README 11→12
|
||
(b) · **M7** acceptance test 11→12 (b) · **M8** compte retiré du README acceptance ⇒
|
||
self-report ÉVAPORÉ. Arbre restauré (`git status` propre). Suites unittest re-vertes :
|
||
deploy_runbook **29 tests OK** · acceptance **37 tests OK** (générateurs inchangés).
|
||
Suite complète re-verte : `check_artifacts` · `check_docs` · `guard_constraints` ·
|
||
`check_ci_integrity` · `check_regression` · `check_readme_claims` tous exit 0.
|
||
**Les DEUX familles de ledger sont désormais gatées ; re-scanner tout NOUVEAU
|
||
dialecte de marqueur (préfixe/phrasé) avant de re-déclarer la classe close.**
|
||
|
||
## Sprint 8 · buffer · RBAC SPEC §2 — ancrage de l'encadré « Défense en profondeur » (NOM du rôle détenteur de `set_user_permissions` · scope · famille) sur l'artefact byte-gaté
|
||
|
||
**Contexte** — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention »). Le §2
|
||
« Modèle RBAC » de `05_deliverables_mvp/rbac/RBAC_50_ROLES_SPEC.md` ferme sur un
|
||
encadré de sécurité « **Défense en profondeur** » : le pouvoir `set_user_permissions`
|
||
(élévation de privilège) est « **réservé** au SEUL rôle `OTO Plateforme RBAC Admin`
|
||
(`scope_donnees = groupe`, `famille = plateforme`) — séparation des pouvoirs vérifiée
|
||
par test automatisé ». La SPEC se réclame donc d'un **fait de sécurité tiré du
|
||
contrat** `rbac_50_roles.json`, sur **trois attributs** : NOM du détenteur · scope ·
|
||
famille. Or le test cité (`test_rbac::test_set_user_permissions_reserve_admin`) impose
|
||
bien scope==groupe ET famille==plateforme à TOUT détenteur — MAIS **jamais le NOM du
|
||
rôle** (`OTO Plateforme RBAC Admin`) NI cette prose §2. L'**UNICITÉ** (« au SEUL rôle »)
|
||
et le NOM étaient DÉJÀ gatés côté **artefact** (bloc « RBAC fixtures », singleton
|
||
`len(fx_sup)==1` depuis `custom_docperm.json` byte-gaté + prose du **README fixtures**
|
||
« émis que pour le rôle **NAME** ») — mais **PAS la 3ᵉ surface prose, le §2 de la SPEC**.
|
||
|
||
**Le « vert trompeur »** — RENOMMER le détenteur dans le contrat (+régénérer
|
||
fixtures/out + audit 4Big) laisse le §2 citer un rôle **DISPARU** en silence :
|
||
`check_artifacts` ne voit qu'un **HASH de TAILLE** du doc (agnostique au nom · la SPEC
|
||
n'est pas un `out/*.json`), les 10 tests restent verts (scope/famille inchangés).
|
||
**Prouvé (M6 SILENT-GREEN)** : renommer `…RBAC Admin`→`…Securite Admin` dans le
|
||
contrat + régénérer les 4 générateurs RBAC & l'audit ⇒ **`check_artifacts` VERT**
|
||
pendant que le nouveau gate mord « §2 nomme ['OTO Plateforme RBAC Admin'] MAIS
|
||
set_user_permissions réservé à 'OTO Plateforme Securite Admin' » — preuve que
|
||
l'autorité est l'**ARTEFACT byte-gaté**, pas la cohérence interne de la SPEC.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, **extension** du bloc « RBAC fixtures »,
|
||
points 4 & 5 — pas un bloc neuf) — RÉUTILISE `fx_sup` (le singleton déjà byte-gaté
|
||
ci-dessus · **zéro re-dérivation de l'unicité**, interdit #5 « jamais de doublon »)
|
||
comme **NOM-autorité** et `src_roles` (contrat byte-gaté) pour scope/famille. Capte
|
||
l'encadré `**Défense en profondeur** … vérifiée par test automatisé` (spec_rd
|
||
whitespace-normalisé) et exige : le pouvoir `set_user_permissions` y est **nommé** ;
|
||
**(a)** le NOM du détenteur cité (accents/casse neutralisés) == le seul porteur du
|
||
flag ; **(b)/(c)** les `scope_donnees = …` / `famille = …` cités == attributs du
|
||
détenteur dans `rbac_50_roles.json` ; **(5)** §6 (row « Séparation des pouvoirs »)
|
||
cite bien `set_user_permissions` réservé (anti-évaporation). Encadré/row INTROUVABLE =
|
||
échec (#6). La glose « (gestion des habilitations) » N'est PAS gatée (éditoriale).
|
||
|
||
**Note process** (interdit #5) — un premier jet ajoutait un bloc §2 autonome
|
||
RECALCULANT le singleton depuis `src_roles` : **doublon** de la vérification d'unicité
|
||
déjà portée par le bloc fixtures (via `custom_docperm.json` byte-gaté, équivalent par
|
||
`check_artifacts`). Écarté ; les 4 checks réécrits en **extension** du bloc existant,
|
||
sans ré-affirmer l'invariant d'unicité.
|
||
|
||
**Vérif** — 4 checks verts sur l'arbre propre. **6 morsures** adversariales, gate
|
||
lancé seul, reverts par `cp`/`tar` ciblés (jamais `git checkout` large — cf. incident
|
||
consigné) : **M1** §2 renomme le détenteur (prose) ⇒ NOM ≠ artefact · **M2** §2
|
||
scope `groupe`→`entite` ⇒ (b) ≠ détenteur · **M3** §2 famille `plateforme`→`ventes`
|
||
⇒ (c) ≠ détenteur · **M4** encadré supprimé ⇒ INTROUVABLE · **M5** §6 retire
|
||
`set_user_permissions` ⇒ ancre évaporée · **M6 SILENT-GREEN** (ci-dessus) contrat
|
||
renommé + artefacts régénérés, `check_artifacts` VERT ⇒ §2 (a) mord. Suite complète
|
||
re-verte : `check_artifacts` · `check_docs` · `guard_constraints` ·
|
||
`check_ci_integrity` · `check_regression` · `check_readme_claims` tous exit 0.
|
||
|
||
## Sprint 8 · buffer · Traçabilité roadmap — ancrage de la CITATION `roadmap_ref` VERBATIM (n° ligne + libellé) des artefacts sur la ligne RÉELLE de la roadmap
|
||
|
||
**Contexte** — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention · toujours
|
||
vérifier sources archives »). Le pattern « citation vérifiée, pas déclarée » (INV11
|
||
de `qa/acceptance` ancre les `roadmap_line` NUMÉRIQUES de son artefact au bullet
|
||
DELIVERABLE réel) portait un angle mort : **quatre** modules — `demo/scenarios`,
|
||
`frontend/chat_otoia`, `frontend/portails`, `mobile/app_config` — publient dans leur
|
||
`out/MANIFEST.json` (byte-gaté) un champ `roadmap_ref` de forme
|
||
`« …ROADMAP… · Sprint N l.A[-B] « <libellé bullet> » »`, où le **libellé est une
|
||
transcription VERBATIM** du bullet de la roadmap à la ligne citée. Or **aucun gate**
|
||
ne vérifiait que la ligne A[-B] porte réellement ce texte : `check_artifacts` ne
|
||
prouve QUE `spec==MANIFEST` byte-for-byte (la MÊME citation recopiée des deux côtés,
|
||
jamais contredite) ; INV11 ne touche QUE les `roadmap_line` numériques de SON
|
||
artefact ; l'ancrage Expo mobile ne teste que « roadmap » + « Expo N » dans
|
||
`expo_sdk_source`, pas la citation complète.
|
||
|
||
**Le « vert trompeur »** — RÉORDONNER la roadmap (les n° de ligne glissent) ou
|
||
REFORMULER un bullet laisse ces 4 citations pointer une ligne qui dit AUTRE CHOSE :
|
||
un agent lisant le MANIFEST pour retrouver la promesse d'origine tomberait sur le
|
||
mauvais bullet. **Prouvé par mutation** : reformuler le bullet l.63 (`Chat OTOIA
|
||
embedded…`) laissait les **6 gates VERTS** pendant que `chat_otoia/out/MANIFEST.json`
|
||
citait encore « … Chat OTOIA embedded … » à l.63. Aucune suite `tests/` (FONCTIONS
|
||
de résolution, jamais l'ancre à la roadmap) ne mord.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, bloc « Traçabilité roadmap », juste
|
||
avant `sys.exit`) — DÉCOUVRE tous les `out/MANIFEST.json` (glob 1 & 2 niveaux)
|
||
portant un `roadmap_ref` citant `(l.A[-B] «…»)` et exige, par module : **(1)** le
|
||
fichier cité == la roadmap canonique ; **(2)** le libellé, NORMALISÉ (markdown
|
||
`**`/`- ` retirés · espaces compressés · NFC), est SOUS-CHAÎNE des lignes A..B
|
||
réelles (robuste à la troncature du bullet et à la plage multi-lignes `l.56-57`) ;
|
||
**(3)** le spec du module porte le MÊME `roadmap_ref` (identité spec↔artefact publié).
|
||
Bornes de ligne hors-plage attrapées (renumérotation). Les **4 modules connus** sont
|
||
EXIGÉS présents (anti-évaporation #6 : retirer un `roadmap_ref` échoue) ; tout AUTRE
|
||
MANIFEST à citation est ancré en bonus (future-proof).
|
||
|
||
**Vérif** — 5 checks verts sur l'arbre propre (4 citations ancrées + recensement
|
||
« 4 modules attendus présents »). **7 morsures** adversariales, gate lancé SEUL,
|
||
reverts par `cp` ciblé (jamais `git checkout` large — cf. incident consigné) :
|
||
**M1 SILENT-GREEN** — reformuler le bullet l.63 (AUTORITÉ), MANIFEST inchangé ⇒
|
||
libellé ABSENT (preuve que l'autorité est la ROADMAP, pas la cohérence interne de
|
||
l'artefact) · M2 renuméroter le MANIFEST `l.63→l.62` (ligne existante mais autre
|
||
bullet) ⇒ ABSENT · M3 dériver le libellé du MANIFEST lui-même ⇒ ABSENT · M4 citer
|
||
un autre fichier roadmap ⇒ fichier ≠ canonique · M5 dériver la citation du SEUL spec
|
||
⇒ spec ≠ MANIFEST (artefact incohérent) · M6 retirer `roadmap_ref` du MANIFEST ⇒
|
||
ÉVAPORÉE ['frontend/chat_otoia'] · M7 citer `l.9999` ⇒ plage hors roadmap (1..92).
|
||
Suite complète re-verte : `check_artifacts` · `check_docs` · `guard_constraints` ·
|
||
`check_ci_integrity` · `check_regression` · `check_readme_claims` tous exit 0.
|
||
|
||
## Sprint 8 · buffer · RBAC SPEC §6 — ancrage de l'ÉNUMÉRATION des DocTypes `custom` sur le catalogue du contrat (2ᵉ surface prose)
|
||
|
||
**Contexte** — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention »). Le
|
||
générateur `rbac_fixtures_gen` émet `MANIFEST.custom_doctypes_a_confirmer` =
|
||
l'ensemble des DocTypes `custom: true` du contrat `rbac_50_roles.json` (les DocTypes
|
||
DTP **à créer** côté VPS avant import — `Faisabilité`, `Publiciste Log`, `CONFOTUR
|
||
Application`, `API Access`). Cet ensemble data-derived est transcrit À LA MAIN à
|
||
**deux** surfaces prose : (1) `rbac/fixtures_gen/README.md` — **déjà gatée** (bloc
|
||
« RBAC fixtures · catalogue custom ») ; (2) `RBAC_50_ROLES_SPEC.md §6` l.117
|
||
(« **DocTypes `custom: true`** (`Faisabilité`, `Publiciste Log`, `CONFOTUR
|
||
Application`, `API Access`) ») — **NON gatée**. Le bloc `apply_plan` ne gate que le
|
||
COMPTE (`len(custom_doctypes)`), aveugle aux NOMS ; `check_artifacts` est aveugle
|
||
(la SPEC n'est pas un `out/*.json`).
|
||
|
||
**Le « vert trompeur »** — AJOUTER un DocType `custom` au contrat (le MANIFEST
|
||
rebâtit l'entrée) ou RENOMMER/ÉCHANGER l'un des 4 laisse §6 à ses 4 noms périmés en
|
||
silence : la cible d'application VPS énumérée par la SPEC ne correspond plus aux
|
||
DocTypes que l'agent ERPNext doit réellement créer. Vérifié par mutation : dropper
|
||
`Publiciste Log` de §6 laissait le gate à **exit 0**. Même patron que la 2ᵉ surface
|
||
CONFOTUR/entités (README gaté, fiche non).
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, extension du bloc « RBAC fixtures »,
|
||
point 3) — RÉUTILISE `fx_cdt` (= `MANIFEST.custom_doctypes_a_confirmer`, byte-gaté,
|
||
déjà dérivé par le point 1) et le normaliseur d'accents `_fx_acc` (zéro duplication
|
||
du contrat) : capte l'énumération §6 (`DocTypes `custom: true`** (…)` sur le texte
|
||
whitespace-normalisé, robuste au retour-ligne 117↔118), extrait les tokens
|
||
backtickés, et exige set-diff EXACT == catalogue MANIFEST (absent ET en trop, casse/
|
||
accents neutralisés). Énumération INTROUVABLE = échec (anti-évaporation #6).
|
||
|
||
**Vérif** — 1 check vert sur l'arbre propre (les 4 == MANIFEST). **5 morsures**
|
||
adversariales : A dropper `Publiciste Log` ⇒ absent · B renommer `API Access`→`API
|
||
Acces` ⇒ absent+en-trop · C ajouter `Ghost DocType` fantôme ⇒ en-trop · **D
|
||
SILENT-GREEN : le MANIFEST GAGNE un 5ᵉ DocType custom (§6 inchangé) ⇒ mord « absents=
|
||
[new custom dt] » — preuve que l'autorité est l'ARTEFACT, pas la cohérence interne
|
||
de la SPEC** · E énumération §6 supprimée ⇒ INTROUVABLE. Reverts par chemin ciblé
|
||
(jamais `git checkout` large — cf. incident consigné). Suite complète re-verte :
|
||
`check_artifacts` · `check_docs` · `guard_constraints` · `check_ci_integrity` ·
|
||
`check_regression` · `check_readme_claims` tous exit 0.
|
||
|
||
**Note process** — un candidat initial (ancrage des attributs de rôle des fiches
|
||
`mobile`/`rendu`/`ifc_speckle`) s'est révélé DÉJÀ couvert par les blocs existants
|
||
« Fiche Mobile » / « Fiches Faisabilité » : la ligne d'index `MEMORY.md` était
|
||
périmée vs le corps détaillé de la mémoire (classe close le 2026-08-01). Doublon
|
||
écarté (interdit #5 « jamais accumuler doublons »), index mémoire corrigé.
|
||
|
||
---
|
||
|
||
## Sprint 8 · buffer · RBAC SPEC §2 — ancrage de l'ÉNUMÉRATION des `scope_donnees` (ligne Row-level) sur l'ENUM du schéma
|
||
|
||
**Contexte** — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention de
|
||
chiffres »). La table §2 « Modèle RBAC » de
|
||
`05_deliverables_mvp/rbac/RBAC_50_ROLES_SPEC.md` ouvre par « trois niveaux, **tous
|
||
couverts par le schéma** » puis sa 3ᵉ ligne (« **Row-level** (User Permission) |
|
||
`scope_donnees` | … ») ÉNUMÈRE à la main l'ensemble EXHAUSTIF des portées
|
||
row-level : `own` · `equipe` · `entite` (Company) · `groupe` (consolidé). Par sa
|
||
phrase d'intro, cet ensemble EST l'`enum` `scope_donnees` de `rbac.schema.json`
|
||
(la DÉFINITION du modèle). Or **aucun gate ne le touchait** : §3 compte les rôles
|
||
par portail, §4 les entités, §5 le résumé, et le bloc `userperm` gate la table
|
||
`scope → mécanisme` d'un **autre** artefact (`userperm_gen/out`) — tous aveugles à
|
||
l'énumération du §2. Même classe que §4 (colonne ancrée sur une source-de-vérité
|
||
structurée), côté schéma cette fois.
|
||
|
||
**Le « vert trompeur »** — si le schéma AJOUTE une portée (`region` pour un besoin
|
||
ops réel) ou en RETIRE une (`own` supprimé, schéma resserré), le §2 reste PÉRIMÉ en
|
||
silence : il documente un modèle qui CONTREDIT le schéma tout en se réclamant
|
||
« couvert par le schéma ». Ni `check_artifacts` (la SPEC n'est pas un `out/*.json`)
|
||
ni `tests/` (FONCTIONS de validation, jamais l'ancre SPEC↔schéma) ne mordent.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, bloc « 1octies », juste après le §5)
|
||
— charge l'`enum` `scope_donnees` depuis `rbac.schema.json` (source de vérité du
|
||
modèle) puis exige : **(a)** ENSEMBLE des jetons `scope` backtickés de la ligne §2
|
||
== enum (set-diff absent ET fantôme) ; **(b)** ANCRAGE — la colonne du milieu NOMME
|
||
bien `scope_donnees` (sinon on gate la mauvaise ligne) ; **(c)** sanity — toute
|
||
portée RÉELLEMENT exercée par le contrat ∈ enum (une portée en usage jamais dans
|
||
l'enum = schéma/contrat incohérents). Les gloses parenthétiques `(Company)`/
|
||
`(consolidé)` NE sont PAS gatées (éditoriales · #6). Ligne/section/enum absents =
|
||
échec (traçabilité #6).
|
||
|
||
**Vérif** — 3 checks verts sur l'arbre propre (4 portées `own/equipe/entite/groupe`
|
||
== enum). **6 morsures** adversariales : M1 §2 retire `own` ⇒ manquant (a) · M2 §2
|
||
ajoute fantôme `region` ⇒ fantôme (a) · **M3 SILENT-GREEN : le schéma GAGNE `region`,
|
||
§2 inchangé ⇒ (a) mord `manquant region` — preuve que l'autorité est le SCHÉMA, pas
|
||
la cohérence interne de la SPEC** · M4 colonne du milieu renommée `scope_donnees`→
|
||
`scope` ⇒ ancre perdue (b) · M5 ligne Row-level supprimée ⇒ INTROUVABLE · M6 le
|
||
schéma RETIRE `own` (encore exercé par 2 rôles) ⇒ (a) fantôme + (c) portée hors
|
||
enum. Gates re-verts : `check_artifacts` · `check_docs` · `guard_constraints` ·
|
||
`check_ci_integrity` · `check_regression` · `check_readme_claims` tous exit 0.
|
||
|
||
---
|
||
|
||
## Sprint 8 · buffer · Faisabilité — ancrage du DOMAINE DES CODES PROJET (garde d'entrée `_validate_brief` + 2 schémas JSON) sur CLAUDE.md §Projets
|
||
|
||
**Contexte** — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention de
|
||
chiffres »). La classe « constante constitutionnelle recopiée dans une GARDE DE
|
||
VALIDATION en code, jamais ancrée » (déjà couverte pour le CONTRAT DE FORMAT
|
||
e-NCF de `ncf.py`) portait un candidat resté NON gaté et paradoxalement au **cœur
|
||
du Livrable Sprint 3** (« P07 Aqua Terra <1h ») : le **DOMAINE des codes projet
|
||
valides** du générateur 4 volets. Ce domaine EST exactement l'ensemble
|
||
`CLAUDE.md §Projets` (P01..P09), recopié À LA MAIN à **QUATRE surfaces**, aucune
|
||
ancrée, toutes invisibles à `check_artifacts` (ni `.py` ni schémas ne sont des
|
||
`out/*.json`) :
|
||
1. `faisabilite/generator/faisabilite_gen.py::_validate_brief` — la GARDE runtime
|
||
`code[:2] == "P0" and code[2] in "123456789"` (le domaine EFFECTIF) + le message
|
||
`« … invalide (attendu P01..P09) »` (les bornes) ;
|
||
2. `faisabilite/version.schema.json` — `properties.projet.pattern "^P0[1-9]$"`
|
||
(validation du `version.json` par projet) + sa `description` ;
|
||
3. `faisabilite/generator/brief.schema.json` — même `pattern "^P0[1-9]$"`
|
||
(validation du brief d'entrée) + sa `description` (qui n'en avait **aucune**).
|
||
|
||
**Le « vert trompeur »** — si CLAUDE.md §Projets AJOUTE un 10ᵉ projet (`P10 …`,
|
||
évènement de portefeuille réel), la garde ET les deux patterns REJETTENT `P10`
|
||
comme « invalide (attendu P01..P09) » : le générateur **REFUSE de scaffolder un
|
||
projet LÉGITIMEMENT mandaté** (bug d'exécution) pendant que la suite `tests/`
|
||
reste VERTE (oracles P01/P02 self-consistants, jamais comparés à §Projets).
|
||
Symétriquement, RETIRER un projet laisse la garde trop PERMISSIVE (accepte un code
|
||
démobilisé). Ni `check_artifacts` (schémas/`.py` ≠ `out/*.json`) ni `tests/`
|
||
(teste la FONCTION `_validate_brief`, pas son ANCRAGE à §Projets) ne mordent.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, bloc « Faisabilité/generator ·
|
||
DOMAINE DES CODES PROJET », juste après « Fiches #10 ») — RE-DÉRIVE le catalogue
|
||
de codes depuis `CLAUDE.md §Projets` (`\bP\d{2,}\b` sur la ligne `## Projets` ·
|
||
source unique) puis exige que chaque surface accepte **EXACTEMENT** cet ensemble :
|
||
(a) la garde `_validate_brief` — reconstruit l'ensemble ACCEPTÉ `{préfixe+chiffre}`
|
||
depuis le code source et set-diff == §Projets ; le message cite les bornes
|
||
min..max ; (b)/(c) les 2 patterns JSON — **test COMPORTEMENTAL** (compile le
|
||
pattern, l'applique à une sonde `§Projets ∪ {P00,P10,P99,P0,PXX,P1,P010,Q01}`,
|
||
exige accepté == §Projets · robuste à la forme de la classe de caractères) + la
|
||
`description` DÉCLARE l'ancrage « CLAUDE.md §Projets » et cite les bornes. Une
|
||
surface INTROUVABLE échoue AUSSI (ancre morte · anti-évaporation #6). Deux
|
||
descriptions de schéma enrichies pour porter l'ancrage (brief.schema en gagne une).
|
||
|
||
**Vérif** — 6 checks verts sur l'arbre propre. **6 morsures** adversariales :
|
||
M1 garde retire le chiffre `7` (validateur trop restrictif) ⇒ set ≠ · M2 message
|
||
`P01..P09→P01..P08` ⇒ bornes ≠ · M3 pattern version élargi `^P0[0-9]$` ⇒ accepte
|
||
`P00` en trop · M4 brief.schema perd l'ancrage §Projets ⇒ ne DÉCLARE plus · **M5
|
||
SILENT-GREEN : CLAUDE.md §Projets ajoute `P10` SEUL (les 4 surfaces inchangées) ⇒
|
||
CASCADE sur les 6 lignes (garde accepte≠, 2 patterns manquent P10, message +2
|
||
descriptions bornes P09≠P10) — preuve que l'autorité est §Projets, pas la
|
||
cohérence interne** · M6 version.schema perd les bornes de sa description. Les
|
||
**16 tests unittest** du générateur restent verts (garde inchangée). Gates
|
||
re-verts : `check_artifacts` · `check_docs` · `guard_constraints` ·
|
||
`check_ci_integrity` · `check_regression` · `check_readme_claims` tous exit 0.
|
||
|
||
---
|
||
|
||
## Sprint 8 · buffer · RBAC SPEC §3 — ancrage de la colonne « Familles rattachées » PAR portail sur le contrat
|
||
|
||
**Contexte** — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention de
|
||
chiffres »). La table §3 « Cartographie portails ↔ familles » de
|
||
`05_deliverables_mvp/rbac/RBAC_50_ROLES_SPEC.md` porte DEUX colonnes data-derived
|
||
du contrat `rbac_50_roles.json` : « Nb rôles » (déjà gatée, bloc §3 count) et —
|
||
juste à sa gauche — « **Familles rattachées** » : pour chaque portail, l'ENSEMBLE
|
||
des `famille` distinctes de ses rôles (ex. `direction` → {direction, finance,
|
||
faisabilite, legal}). Cette 2ᵉ colonne était **NON gatée** : le bloc §3 count la
|
||
capture avec `[^|]*` (il ne lit QUE le compte), le §5 « résumé » traite volontairement
|
||
les regroupements comme ÉDITORIAUX (pas de mapping famille↔groupe, cf. memory
|
||
`rbac-spec-portail-count-gate`), et `check_artifacts` est aveugle (la SPEC n'est
|
||
pas un `out/*.json`). Même classe que « colonne SET gatée seulement sur son COUNT
|
||
voisin » (roleprofile per-profil count vs quels rôles).
|
||
|
||
**Le « vert trompeur »** — reclasser la `famille` d'un rôle À L'INTÉRIEUR d'un
|
||
portail change le SET de familles **sans** changer le « Nb rôles » du portail → la
|
||
cellule §3 ment en silence pendant que le count-gate reste VERT. Prouvé (M4) :
|
||
reclasser les DEUX rôles `legal` de `direction` (Directeur Juridique + Agent ONAPI)
|
||
en `finance` laisse le compte à **9==9 VERT** tandis que le nouveau gate mord
|
||
**FANTÔME {legal}** (la SPEC liste encore `legal`, le contrat ne l'a plus).
|
||
Ajouter/retirer une famille (rôle d'une famille inédite) laisse aussi la cellule
|
||
périmée. Aucune suite `tests/` (FONCTIONS de validation, pas l'ancre SPEC↔contrat)
|
||
ne mord.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, bloc « 1quater-bis » juste après le
|
||
§3 count-gate, RÉUTILISE `src_roles`/`spec_rd` déjà chargés) — RECOMPUTE le SET de
|
||
familles distinctes PAR portail depuis `rbac_50_roles.json[roles][].famille` et
|
||
exige, par ENSEMBLE (absent ET fantôme), l'égalité avec les jetons `famille` de la
|
||
cellule. On ne gate QUE les jetons `famille` bruts (identifiant de tête de chaque
|
||
item séparé par `,`, via `(?:^|,)\s*([a-z]+)`), **PAS** les parenthèses ÉDITORIALES
|
||
(« finance (CFO) », « faisabilite (analyste/rendu/IFC/économiste) » — paraphrase,
|
||
pas contrat, #6). Ordre NON gaté (réordonner les rôles dans le contrat byte-gaté
|
||
est sémantiquement neutre pour l'appartenance). Un portail sans cellule échoue AUSSI
|
||
(INTROUVABLE = régression de traçabilité #6).
|
||
|
||
**Vérif** — 6 checks verts sur l'arbre propre (un par portail). **5 morsures**
|
||
adversariales : M1 SPEC retire `legal` de `direction` ⇒ MANQUE {legal} · M2 SPEC
|
||
ajoute `rh` à `achat` ⇒ FANTÔME {rh} · M3 SPEC `finance`→`financ` sur `compta` ⇒
|
||
MANQUE {finance} · FANTÔME {financ} · **M4 SILENT-GREEN : contrat reclasse les 2
|
||
rôles `legal`→`finance` dans `direction`, compte inchangé (count-gate 9==9 VERT) ⇒
|
||
familles-gate mord FANTÔME {legal} — preuve que l'autorité est le CONTRAT, pas la
|
||
cohérence interne de la SPEC** · M5 SPEC supprime la ligne `direction` ⇒ cellule
|
||
INTROUVABLE. Gates re-verts : `check_artifacts` · `check_ci_integrity` ·
|
||
`check_docs` · `check_readme_claims` · `check_regression` · `guard_constraints`
|
||
tous exit 0.
|
||
|
||
---
|
||
|
||
## Sprint 8 · buffer · Faisabilité — ancrage du TEMPLATE CANONIQUE v1.0 (5 surfaces #9/#10) sur CLAUDE.md
|
||
|
||
**Contexte** — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention de
|
||
chiffres »). Le pattern « constante canonique CLAUDE.md recopiée à la main dans
|
||
une surface data-derived » (re-dérive la valeur depuis la contrainte, jamais la
|
||
cohérence interne) couvre déjà le générateur (`model.py`/README/renderer/scorer/
|
||
oracle-test), les fiches ERPNext/CRM (« Fiches #10 »), le module fiscal (`moneda`),
|
||
la marque SEO §Entités, la persona Chat OTOIA, les tokens branding #4, etc. Un
|
||
fichier restait NON gaté (grep : absent de tous les `ci/*.sh`) et c'est
|
||
paradoxalement **le plus normatif** : `05_deliverables_mvp/faisabilite/
|
||
TEMPLATE_FAISABILITE_CANONIQUE_v1.0.md`, le DOCUMENT-MODÈLE « CANONIQUE » (Livrable
|
||
Sprint 1) que **toute faisabilité en production DOIT reproduire**, et que le
|
||
générateur 4 volets + le Publiciste + la régénération batch S4 sont censés suivre.
|
||
|
||
Ce template recopie **à la main les 6 valeurs de CLAUDE.md #9/#10 à CINQ surfaces
|
||
structurées**, toutes se réclamant de « contrainte #9 »/« #10 », **aucune gatée** :
|
||
1. **l.28** intro §0 « frais **3 %** · marketing **8.5 %** · point d'équilibre
|
||
**52 %** » (3 pourcentages #9, associés par mot-clé) ;
|
||
2. **l.144-146** §5.2 — 3 lignes de table « **N %** (fixe · contrainte #9) » ;
|
||
3. **l.29** intro §0 « **USD + DOP** · format **Letter US** · paiements **Cardnet** » ;
|
||
4. **l.149-151** §5.2 — 3 lignes « **valeur** (… contrainte #10) » (la ligne
|
||
paiement citant l'exclusion « ❌ pas-X ») ;
|
||
5. **l.196** §7 checklist « 3 % / 8.5 % / 52 % · USD+DOP · Letter US · Cardnet
|
||
présents » (énumération des 6 marqueurs).
|
||
|
||
**Le « vert trompeur »** — si CLAUDE.md #9 passe **52 %→55 %** (ou #10 renomme le
|
||
prestataire) et que les modules s'alignent, ce TEMPLATE — la RÉFÉRENCE de toutes
|
||
les faisabilités — reste PÉRIMÉ en silence, imposant à chaque projet un paramètre
|
||
qui CONTREDIT le mandat pendant qu'il se réclame « contrainte #9/#10 ». Ni
|
||
`check_artifacts` (ce n'est pas un `out/*.json`) ni `check_docs`/`guard_constraints`
|
||
ni aucune suite `tests/` (FONCTIONS, jamais l'ancre à CLAUDE.md) ne mordent.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, bloc « TEMPLATE FAISABILITÉ CANONIQUE
|
||
v1.0 · PARAMÈTRES #9/#10 », juste après « Fiches #10 ») — RÉUTILISE `exp` (les 6
|
||
valeurs déjà recomputées des lignes #9/#10 de CLAUDE.md · zéro re-dérivation) +
|
||
`exp_excl` (prestataire exclu du `(pas X)`). Exige que chacune des 5 surfaces ==
|
||
CLAUDE.md : (1) 3 pcts associés par mot-clé ; (2) SET des 3 lignes « (fixe ·
|
||
contrainte #9) » == {3 %,8.5 %,52 %} (mord aussi ligne ajoutée/retirée) ; (3)/(4)
|
||
chaque valeur #10 présente en gras dans sa région ancrée + l'exclusion ❌ ; (5)
|
||
énumération des 6 marqueurs == ensemble EXACT (set-diff absent ET en trop). Une
|
||
surface INTROUVABLE échoue AUSSI (recensement évaporé = régression #6).
|
||
|
||
**Vérif** — 5 checks verts sur l'arbre propre. **11 morsures** adversariales :
|
||
M1 intro#9 52→55 · M2 ligne#9 8.5→9 · M3 intro#10 Cardnet→Azul · M4 ligne#10
|
||
Letter US→A4 · M5 exclusion ❌ Stripe→PayPal · M6 checklist perd Cardnet · M7
|
||
checklist 52→60 · M8 intro#9 supprimée (INTROUVABLE) · M9 checklist « présents »→
|
||
« présent » (INTROUVABLE) · **M10 CLAUDE.md #9 52→55 SEUL ⇒ cascade sur 3 régions
|
||
(1)(2)(5)** · **M11 CLAUDE.md #10 Cardnet→Azul SEUL ⇒ cascade sur 3 régions
|
||
(3)(4)(5)** — les deux preuves que l'AUTORITÉ est CLAUDE.md, pas la cohérence
|
||
interne du template. Un piège corrigé en route : le groupe capturé est le NOMBRE nu
|
||
(sans `%`), donc `_pct()` (qui exige un `%`) renvoyait `None` → `float()` direct.
|
||
Gates re-verts : `check_artifacts` · `check_docs` · `guard_constraints` ·
|
||
`check_ci_integrity` · `check_regression` · `check_readme_claims` tous exit 0.
|
||
|
||
---
|
||
|
||
## Sprint 8 · buffer · Fiscal e-CF — ancrage du CONTRAT DE FORMAT e-NCF dans le COMPOSEUR `ncf.py` (CODE) + son ORACLE de test
|
||
|
||
**Contexte** — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention de
|
||
chiffres »). Le bloc « Fiscal · e-CF DGII · CONTRAT DE FORMAT » existant ancre le
|
||
format de l'identifiant fiscal DGII (`E + tipoeCF(2) + secuencia(10)` = 13,
|
||
Ley 32-23) sur le SEUL bloc `e_ncf` byte-gaté de `out/ecf_plan.json` (dérivé de
|
||
`ecf_spec.json`) et couvre **4 surfaces de PROSE** : README ×3 + fiche
|
||
`erpnext_backend`. Mais le **composeur d'exécution lui-même** — `ecflib/ncf.py`,
|
||
le code Frappe qui VALIDE et COMPOSE réellement le numéro e-NCF côté VPS/Compupar
|
||
— porte **TROIS copies INDÉPENDANTES du format, AUCUNE dérivée de l'artefact**, +
|
||
un oracle de test qui les bénit, tous **NON gatés** :
|
||
|
||
1. `ENCF_RE = re.compile(r"^E([0-9]{2})([0-9]{10})$")` (l.26) — la **regex de
|
||
VALIDATION** (cœur de `is_valid_encf`/`parse_encf`) ;
|
||
2. `FORMULE = "E + tipoeCF(2) + secuencia(10)"` (l.27) — la chaîne **publiée par
|
||
`compose_encf`** (renvoyée au caller VPS) ;
|
||
3. docstring l.8 « `"E" + tipoeCF (2 chiffres) + secuencia (10 chiffres) → 13
|
||
caracteres` » + commentaire l.25 « Ley 32-23 : E + tipo(2) + secuencia(10) =
|
||
13 » (deux proses-en-code) ;
|
||
4. `tests/test_ecf_dgii.py:246` — oracle hardcodé `assertEqual(…["formule"],
|
||
"E + tipoeCF(2) + secuencia(10)")`.
|
||
|
||
**Le « vert trompeur »** — un rango DGII à 12 chiffres (évènement ops réel, cité
|
||
dans le commentaire du gate existant) régénère `ecf_spec→ecf_plan`, donc les 4
|
||
proses gatées SUIVENT. Mais `ENCF_RE` reste `{2}{10}` : `compose_encf` zfill à 10
|
||
et `is_valid_encf` **REJETTE** un e-NCF désormais valide → **bug d'exécution du
|
||
composeur fiscal**, pendant que la suite `tests/` reste **VERTE** (son oracle ==
|
||
`FORMULE` périmée, self-consistante). Ni `check_artifacts` (`ncf.py` n'est pas un
|
||
`out/*.json`) ni `tests/` (teste les FONCTIONS, pas leur ANCRAGE à l'artefact) ne
|
||
mordent. Même patron que l'oracle/formateur du Publiciste déjà gaté.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, extension du bloc e-NCF · surfaces
|
||
5→9) — RÉUTILISE `prefix/tipo_len/seq_len/total` déjà dérivés du bloc `e_ncf`
|
||
byte-gaté + le helper `ecf_check` (zéro re-dérivation du format #6). Ancre les 5
|
||
surfaces CODE/test au SEUL artefact : (5) `ENCF_RE`, (6) `FORMULE`, (7) docstring,
|
||
(8) commentaire, (9) oracle de test. Une surface absente échoue AUSSI (INTROUVABLE
|
||
· anti-évaporation #6).
|
||
|
||
**Vérif** — 5 checks verts sur l'arbre propre. **7 morsures** adversariales :
|
||
M1 `ENCF_RE {10}→{12}` · M2 `FORMULE secuencia(10)→(12)` · M3 docstring `10→12
|
||
chiffres` · M4 commentaire `secuencia(10)→(12)` · M5 oracle test `(10)→(12)` ·
|
||
**M6 SILENT-GREEN — l'ARTEFACT passe à seq=12 pendant que `ncf.py` reste à 10 ⇒
|
||
les 5 surfaces mordent TOUTES (preuve que l'autorité est l'artefact byte-gaté, pas
|
||
la cohérence interne du composeur)** · M7 `FORMULE` renommée ⇒ INTROUVABLE. Les
|
||
**39 tests unittest** e-CF restent verts (composeur inchangé). Gates re-verts :
|
||
`check_artifacts` · `check_ci_integrity` · `check_docs` · `check_readme_claims` ·
|
||
`check_regression` · `guard_constraints` tous exit 0.
|
||
|
||
---
|
||
|
||
## Sprint 8 · buffer · CRM Commissions — gate d'IDENTITÉ du COMPTE D'INVARIANTS de `_validate` (7ᵉ instance du ledger)
|
||
|
||
**Contexte** — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention de
|
||
chiffres »). Le pattern « ledger d'invariants » (recompute le nombre de contrôles
|
||
CI depuis le registre-source `# N ·` de `_validate`, jamais une liste à la main)
|
||
a déjà couvert **Chat OTOIA**, **SEO**, **Fiscal e-CF**, **Legal CONFOTUR**,
|
||
**CRM Dossier Vente** et **QA Audit 5D**. Le **7ᵉ générateur** — dernier candidat
|
||
restant recensé au ledger (`crm/commissions` ·10) — était NON gaté :
|
||
`crm/commissions/commissions_gen.py` (barème de commissions vendeurs · roadmap l.51).
|
||
|
||
Le compte **« 10 invariants »** y est transcrit **à la main à CINQ endroits**,
|
||
aucun gaté, tous invisibles à `check_artifacts` (le `.py` source n'est pas un
|
||
`out/*.json`) :
|
||
1. message de succès CLI `validate` « … schéma + 10 invariants verts. » (l.209) ;
|
||
2. aide du sous-parseur `validate` « schéma + 10 invariants » (l.221) ;
|
||
3. README bloc de commande « schéma + 10 invariants » (l.60) ;
|
||
4. README titre de section « ## Les 10 invariants » (l.64) ;
|
||
5. README prose de score « 10 invariants de cross-cohérence » (l.92).
|
||
|
||
Ajouter un contrôle `# 11 ·` à `_validate` sans toucher les 5 chaînes ⇒ le README
|
||
ET les self-reports du `.py` mentent en silence pendant que la CI applique 11
|
||
contrôles. Aucune suite `tests/` (qui teste des FONCTIONS de validation, pas la
|
||
prose) n'attrape ce « vert trompeur ».
|
||
|
||
**Particularité du registre Commissions** (≈ Dossier Vente) : le ledger est
|
||
**PROPRE 1..10** — le **schéma est compté À PART** (phrasé « schéma + N
|
||
invariants »), donc pas de schéma-#1 non marqué et pas de split a/b ; on garde la
|
||
détection de doublon nu comme filet. Le module porte AUSSI un **SECOND nombre** —
|
||
« **25 tests** » (la suite unittest, déjà gatée par ailleurs) présent dans le
|
||
README : l'ancre sur le mot « invariants » l'exclut nativement (« 25 tests » ≠
|
||
« invariants ») ⇒ aucun faux positif. Comme Dossier Vente, ce README A un titre
|
||
« ## Les N invariants » ET une énumération numérotée du ledger → on gate (a)(b)(c)(d)(e).
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, bloc « CRM · Commissions · IDENTITÉ
|
||
du COMPTE D'INVARIANTS ») — RECOMPUTE le ledger depuis le SEUL registre-source
|
||
(`# N ·` de `_validate`) et exige :
|
||
- **(a)** ledger CONTIGU 1..N (lacune OU doublon nu = vrai défaut de registre) ;
|
||
- **(b)** toute mention « N invariants » du `.py` == |ledger| ;
|
||
- **(c)** l'annonce du titre README « ## Les N invariants » == |ledger| ;
|
||
- **(d)** toute mention « N invariants » du README == |ledger| ;
|
||
- **(e)** l'ÉNUMÉRATION numérotée de la section (« 1. … 2. … N. ») contiguë 1..N.
|
||
Une mention ou le registre absent échoue AUSSI (traçabilité #6 · anti-évaporation).
|
||
|
||
**Vérif** — 5 checks verts sur l'arbre propre (ledger 1..10 = 10 marqueurs
|
||
`# N ·`, schéma compté à part). **7 morsures** adversariales : M1 self-report
|
||
`.py` 10→11 (b) · M2 titre README 10→11 (c+d) · M3 prose README 10→11 (d) · **M4
|
||
ajout marqueur `# 11 ·` ⇒ ledger→11 pendant que la prose reste 10 (mord `.py` ET
|
||
README titre/mention ET l'énumération, preuve que l'autorité est le CODE)** · M5
|
||
suppression `# 7 ·` ⇒ gap non-contigu (a) · M6 doublon nu `# 5 ·` ⇒ détection de
|
||
vrai doublon (a) · M7 énumération README 7.→8. ⇒ deux 8/pas de 7 (e). Le distinct
|
||
« 25 tests » reste INVISIBLE au gate (ancre « invariants »). Gates re-verts :
|
||
`check_artifacts` · `check_docs` · `guard_constraints` · `check_ci_integrity` ·
|
||
`check_regression` · `check_readme_claims` tous exit 0. **Série ledger close : les
|
||
7 générateurs portant le nombre magique d'invariants sont désormais tous gatés.**
|
||
|
||
---
|
||
|
||
## Sprint 8 · buffer · QA Audit 5D — gate d'IDENTITÉ du COMPTE D'INVARIANTS de `_validate` (6ᵉ instance du ledger)
|
||
|
||
**Contexte** — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention de
|
||
chiffres »). Le pattern « ledger d'invariants » (recompute le nombre de contrôles
|
||
CI depuis le registre-source `# N ·` de `_validate`, jamais une liste à la main)
|
||
a déjà couvert **Chat OTOIA**, **SEO**, **Fiscal e-CF**, **Legal CONFOTUR** et
|
||
**CRM Dossier Vente**. Le **6ᵉ générateur** portant le même nombre magique restait
|
||
NON gaté : `qa/audit_5d/audit_5d_gen.py` (candidat restant recensé : `crm/commissions`
|
||
·10, ledger propre non gaté).
|
||
|
||
Le compte **« 15 invariants »** y est transcrit **à la main à SIX endroits**,
|
||
aucun gaté, tous invisibles à `check_artifacts` (le `.py` source n'est pas un
|
||
`out/*.json`) :
|
||
1. docstring sous-commande module (l.19 « valide schéma + 15 invariants ») ;
|
||
2. docstring `_validate` (l.80 « 15 invariants de cohérence audit/contrôles ») ;
|
||
3. message de succès CLI `validate` « … 15 invariants — verdict … » (l.218) ;
|
||
4. aide du sous-parseur `validate` « valide schéma + 15 invariants » (l.229) ;
|
||
5. README bloc de commande « schéma + 15 invariants » (l.66) ;
|
||
6. README prose d'architecture « CLI build/validate + **15 invariants** » (l.81).
|
||
|
||
Ajouter un contrôle `# 16 ·` à `_validate` sans toucher les 6 chaînes ⇒ le README
|
||
ET les self-reports du `.py` mentent en silence pendant que la CI applique 16
|
||
contrôles. Aucune suite `tests/` (qui teste des FONCTIONS de validation, pas la
|
||
prose) n'attrape ce « vert trompeur ».
|
||
|
||
**Particularité du registre Audit 5D** (≈ Dossier Vente, ≠ Fiscal) : le ledger est
|
||
**PROPRE 1..15** — le **schéma est compté À PART** (phrasé « schéma + N invariants »),
|
||
donc **pas de schéma-#1 non marqué** et **pas de split a/b** ; on garde la détection
|
||
de doublon nu comme filet. MAIS le module porte un **SECOND nombre distinct** —
|
||
« **17 contrôles** » (le compte de fonctions-contrôle de `qalib/controls.py`, déjà
|
||
gaté par ailleurs), présent dans le `.py` ET le README : un `\d+ invariants` ancré
|
||
sur le mot « invariants » l'**exclut nativement** (« 17 contrôles » ≠ mot
|
||
« invariants ») ⇒ **aucun faux positif** sur l'arbre propre. Contrairement à
|
||
Dossier Vente/Fiscal, ce README n'a **NI titre « ## Les N invariants » NI
|
||
énumération numérotée** du ledger → on ne gate que (a)(b)(d).
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, bloc « QA · Audit 5D · IDENTITÉ du
|
||
COMPTE D'INVARIANTS ») — RECOMPUTE le ledger depuis le SEUL registre-source
|
||
(`# N ·` de `_validate`) et exige :
|
||
- **(a)** ledger CONTIGU 1..N (lacune OU doublon nu = vrai défaut de registre) ;
|
||
- **(b)** toute mention « N invariants » du `.py` == |ledger| ;
|
||
- **(d)** toute mention « N invariants » du README == |ledger|.
|
||
Une mention ou le registre absent échoue AUSSI (traçabilité #6 · anti-évaporation).
|
||
|
||
**Vérif** — 3 checks verts sur l'arbre propre (ledger 1..15 = 15 marqueurs `# N ·`,
|
||
schéma compté à part). **6 morsures** adversariales : M1 self-report `.py` 15→16
|
||
(b) · M2 mention README 15→16 (d) · **M3 ajout marqueur `# 16 ·` ⇒ ledger→16 pendant
|
||
que la prose reste 15 (mord `.py` ET README, preuve que l'autorité est le CODE)** ·
|
||
M4 suppression `# 7 ·` ⇒ gap non-contigu (a) · M5 doublon nu `# 5 ·` ⇒ détection de
|
||
vrai doublon (a) · **M6 no-false-positive** : le distinct « 17 contrôles » reste
|
||
INVISIBLE au gate (l'ancre « invariants » l'exclut, arbre propre VERT). Gates
|
||
re-verts : `check_artifacts` · `check_docs` · `guard_constraints` ·
|
||
`check_ci_integrity` · `check_regression` · `check_readme_claims` tous exit 0.
|
||
|
||
---
|
||
|
||
## Sprint 8 · buffer · CRM Dossier Vente — gate d'IDENTITÉ du COMPTE D'INVARIANTS de `_validate` (5ᵉ instance du ledger)
|
||
|
||
**Contexte** — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention de
|
||
chiffres »). Le pattern « ledger d'invariants » (recompute le nombre de contrôles
|
||
CI depuis le registre-source `# N ·` de `_validate`, jamais une liste à la main)
|
||
a déjà couvert **Chat OTOIA**, **SEO**, **Fiscal e-CF** et **Legal CONFOTUR**. Le
|
||
**5ᵉ générateur** portant le même nombre magique restait NON gaté :
|
||
`crm/dossier_vente/doctype_dossier_vente_gen.py` (candidats restants recensés :
|
||
`qa/audit_5d` ·15, `crm/commissions` ·10, tous ledgers propres non gatés).
|
||
|
||
Le compte **« 12 invariants »** y est transcrit **à la main à SEPT endroits**,
|
||
aucun gaté, tous invisibles à `check_artifacts` (le `.py` source n'est pas un
|
||
`out/*.json`) :
|
||
1. docstring module (l.14 « schéma + 12 invariants de ») ;
|
||
2. docstring `_validate` (l.77 « 12 invariants de cross-cohérence ») ;
|
||
3. message de succès CLI `validate` « … schéma + 12 invariants verts. » (l.218) ;
|
||
4. aide du sous-parseur `validate` (l.230) ;
|
||
5. README bloc de commande « schéma + 12 invariants » (l.56) ;
|
||
6. README titre de section « ## Les 12 invariants » (l.60) ;
|
||
7. README prose de score « 12 invariants de cross-cohérence » (l.83).
|
||
|
||
Ajouter un contrôle `# 13 ·` à `_validate` sans toucher les 7 chaînes ⇒ le README
|
||
ET les self-reports du `.py` mentent en silence pendant que la CI applique 13
|
||
contrôles. Aucune suite `tests/` (qui teste des FONCTIONS de validation, pas la
|
||
prose) n'attrape ce « vert trompeur ».
|
||
|
||
**Particularité du registre Dossier Vente** (≈ CONFOTUR, ≠ Fiscal) : le ledger est
|
||
**PROPRE 1..12** — le **schéma est compté À PART** (phrasé « schéma + N invariants »),
|
||
donc **pas de schéma-#1 non marqué** et **pas de split a/b**. On garde néanmoins la
|
||
détection de doublon nu comme filet. Le README porte AUSSI une **énumération
|
||
numérotée** sous le titre de section (item 1 = conformité au schéma, puis les
|
||
contrôles), gatée en contiguïté 1..N.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, bloc « CRM · Dossier Vente · IDENTITÉ
|
||
du COMPTE D'INVARIANTS ») — RECOMPUTE le ledger depuis le SEUL registre-source
|
||
(`# N ·` de `_validate`) et exige :
|
||
- **(a)** ledger CONTIGU 1..N (lacune OU doublon nu = vrai défaut de registre) ;
|
||
- **(b)** toute mention « N invariants » du `.py` == |ledger| ;
|
||
- **(c)** l'annonce du titre README « ## Les N invariants » == |ledger| ;
|
||
- **(d)** toute mention « N invariants » du README == |ledger| ;
|
||
- **(e)** l'ÉNUMÉRATION numérotée de la section (« 1. … 2. … N. ») contiguë 1..N.
|
||
Une mention ou le registre absent échoue AUSSI (traçabilité #6).
|
||
|
||
**Vérif** — 5 checks verts sur l'arbre propre (ledger 1..12 = 12 marqueurs `# N ·`,
|
||
schéma compté à part). **7 morsures** adversariales : M1 self-report `.py` 12→13
|
||
(b) · M2 titre README 12→13 (c+d) · M3 prose README 12→13 (d) · **M4 ajout marqueur
|
||
`# 13 ·` ⇒ ledger→13 pendant que la prose reste 12 (mord `.py` ET README ET
|
||
l'énumération, preuve que l'autorité est le CODE)** · M5 suppression `# 7 ·` ⇒ gap
|
||
non-contigu (a) · M6 doublon nu `# 5 ·` ⇒ détection de vrai doublon (a) · M7
|
||
énumération README 7.→8. ⇒ deux 8/pas de 7 (e). Gates re-verts : `check_artifacts`
|
||
· `check_docs` · `guard_constraints` · `check_ci_integrity` · `check_regression` ·
|
||
`check_readme_claims` tous exit 0.
|
||
|
||
---
|
||
|
||
## Sprint 8 · buffer · Legal CONFOTUR — gate d'IDENTITÉ du COMPTE D'INVARIANTS de `_validate` (4ᵉ instance du ledger)
|
||
|
||
**Contexte** — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention de
|
||
chiffres »). Le pattern « ledger d'invariants » (recompute le nombre de contrôles
|
||
CI depuis le registre-source `# N ·` de `_validate`, jamais une liste à la main)
|
||
a déjà couvert **Chat OTOIA**, **SEO** et **Fiscal e-CF**. Le **4ᵉ générateur**
|
||
portant le même nombre magique restait NON gaté :
|
||
`legal/confotur/confotur_application_gen.py`.
|
||
|
||
Le compte **« 14 invariants »** y est transcrit **à la main à SIX endroits**,
|
||
aucun gaté, tous invisibles à `check_artifacts` (le `.py` source n'est pas un
|
||
`out/*.json`) :
|
||
1. docstring module (l.24 « schéma + 14 invariants de ») ;
|
||
2. docstring `_validate` (l.87 « 14 invariants de cross-coherence ») ;
|
||
3. message de succès CLI `validate` « … schéma + 14 invariants verts. » (l.283) ;
|
||
4. aide du sous-parseur `validate` (l.295) ;
|
||
5. README bloc de commande « schéma + 14 invariants » (l.56) ;
|
||
6. README commentaire d'arborescence « CLI build/validate · 14 invariants » (l.82).
|
||
|
||
Ajouter un contrôle `# 15 ·` à `_validate` sans toucher les 6 chaînes ⇒ le README
|
||
ET les self-reports du `.py` mentent en silence pendant que la CI applique 15
|
||
contrôles. Aucune suite `tests/` (qui teste des FONCTIONS de validation, pas la
|
||
prose) n'attrape ce « vert trompeur ».
|
||
|
||
**Particularité du registre CONFOTUR** (≠ Fiscal) : le ledger est **PROPRE 1..14**
|
||
(pas de schéma-#1 non marqué, pas de split a/b). MAIS le README porte AUSSI un
|
||
compte de **SOUS-ENSEMBLE éditorial** — « **2 invariants** refusent tout champ
|
||
monétaire » (l.92) — à NE PAS confondre avec le total. Un `\d+ invariants` naïf
|
||
mordrait donc l'arbre PROPRE (il verrait `{2, 14}`). Le gate exclut le
|
||
sous-ensemble par **lookahead négatif** `(\d+)\s+invariants(?!\s+refusent)`,
|
||
appliqué au `.py` comme au README.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, bloc « Legal · CONFOTUR · IDENTITÉ
|
||
du COMPTE D'INVARIANTS ») — RECOMPUTE le ledger depuis le SEUL registre-source
|
||
(`# N ·` de `_validate`) et exige :
|
||
- **(a)** ledger CONTIGU 1..N (lacune OU doublon nu = vrai défaut de registre) ;
|
||
- **(b)** toute mention-total « N invariants » du `.py` (hors « refusent ») == |ledger| ;
|
||
- **(c)** toute mention-total du README (hors « refusent ») == |ledger|.
|
||
Une mention ou le registre absent échoue AUSSI (disparition d'un chiffre =
|
||
régression de traçabilité #6).
|
||
|
||
**Vérif** — 3 checks verts sur l'arbre propre (ledger 1..14 = 14 marqueurs `# N ·`).
|
||
**6 morsures** adversariales : M1 self-report `.py` 14→15 (b) · M2 README commande
|
||
14→15 (c) · M3 README arborescence 14→13 (c) · **M4 ajout marqueur `# 15 ·` ⇒
|
||
ledger→15 pendant que la prose reste 14 (mord `.py` ET README, preuve que
|
||
l'autorité est le CODE)** · M5 suppression `# 7 ·` ⇒ gap non-contigu (a) · M6
|
||
doublon nu `# 5 ·` ⇒ détection de vrai doublon (a). **M7 no-false-positive** :
|
||
sous-ensemble « 2 → 3 invariants refusent » reste VERT (le lookahead exclut bien
|
||
le sous-ensemble). Gates re-verts : `check_artifacts` · `check_docs` ·
|
||
`guard_constraints` · `check_ci_integrity` · `check_regression` ·
|
||
`check_readme_claims` tous exit 0.
|
||
|
||
---
|
||
|
||
## Sprint 8 · buffer · Publiciste — gate d'ANCRAGE des DEVISES du site public sur CLAUDE.md #10
|
||
|
||
**Contexte** — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention de
|
||
chiffres »). Le bloc `Publiciste/branding` existant ancre les **4 tokens DESIGN**
|
||
de CLAUDE.md #4 (2 couleurs + 2 typographies) sur leurs 4 copies. Mais le MÊME
|
||
module publiciste porte une **2ᵉ constante constitutionnelle** — les **devises
|
||
CLAUDE.md #10 (« USD + DOP »)** — recopiée à 4 endroits data-derived, **aucun
|
||
gaté**, tous invisibles à `check_artifacts` (pas un `out/*.json`) :
|
||
|
||
1. `lib/branding.py` `DEVISE_PRIMAIRE`/`DEVISE_SECONDAIRE` — constantes auto-
|
||
étiquetées « contrainte #10 · USD + DOP », sans gate ;
|
||
2. `lib/generator.py` `_fmt_usd`/`_fmt_dop` — les **formateurs qui IMPRIMENT le
|
||
code devise sur la grille de prix du site public** `vente.otov7.com` (sortie
|
||
face-public : une devise hors #10 = mandat trahi en silence) ;
|
||
3. `lib/generator.py` footer HTML « Prix … en USD + DOP (Cardnet) » ;
|
||
4. `tests/test_publiciste.py` — l'oracle hardcodé `assertIn("USD 150 000", …)`,
|
||
une copie de plus jamais comparée à CLAUDE.md.
|
||
|
||
Aucune suite `tests/` (qui teste des **fonctions** de rendu, pas l'ancre à
|
||
CLAUDE.md) n'attrape la dérive : renommer la devise #10 et aligner le module
|
||
reste vert alors que le site émettrait la mauvaise devise ; ne rien bouger reste
|
||
vert alors que `branding.py` se dit « #10 » en mentant.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, bloc « Publiciste/generator+branding
|
||
· DEVISES CANONIQUES ») — RE-PARSE les 2 devises depuis la ligne #10 de CLAUDE.md
|
||
(indépendamment de `exp` amont · aucun skip hérité) puis exige :
|
||
- **(a)** `branding.py` DEVISE_PRIMAIRE/SECONDAIRE == #10 (ordre inclus) ;
|
||
- **(b)** l'ensemble des codes émis par les formateurs `generator.py` == #10 ;
|
||
- **(c)** le footer HTML cite les 2 devises dans l'ordre + `(Cardnet)` ;
|
||
- **(d)** l'oracle du test == devise primaire #10.
|
||
Un claim absent échoue AUSSI (traçabilité #6 · anti-évaporation).
|
||
|
||
**Vérif** — 5 checks verts sur l'arbre propre. **6 morsures** adversariales :
|
||
M1 branding primaire USD→EUR · M2 formateur `_fmt_usd`→EUR (hors #10) · M3 footer
|
||
USD+DOP→USD+EUR · M4 oracle test → EUR · **M5 CLAUDE.md #10 USD→EUR = cascade sur
|
||
les 4 copies simultanément** (preuve que l'ancre est CLAUDE.md, pas la cohérence
|
||
interne) · M6 footer supprimé → INTROUVABLE. Gates re-verts :
|
||
`check_readme_claims` · `check_docs` · `guard_constraints` · `check_ci_integrity`
|
||
tous exit 0.
|
||
|
||
---
|
||
|
||
## Sprint 8 · buffer · Fiscal e-CF DGII : gate d'IDENTITÉ du COMPTE D'INVARIANTS de `_validate`
|
||
|
||
**Contexte** — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention de
|
||
chiffres »). Le pattern « ledger d'invariants » a déjà été appliqué à **Chat
|
||
OTOIA** et **SEO** (recompute le nombre d'invariants CI depuis le registre-source
|
||
`# N ·` de `_validate`, jamais une liste à la main). Le **3ᵉ générateur** portant
|
||
le même nombre magique restait NON couvert : `fiscal/ecf_dgii/ecf_dgii_gen.py`.
|
||
|
||
Le compte **« 12 invariants »** y est transcrit **à la main à SEPT endroits**,
|
||
aucun gaté, tous invisibles à `check_artifacts` (le `.py` source n'est pas un
|
||
`out/*.json`) :
|
||
1. `ecf_dgii_gen.py` docstring module (l.24) ;
|
||
2. docstring `_validate` (l.110) ;
|
||
3. message de succès CLI `validate` « … schéma + 12 invariants verts. » (l.277) ;
|
||
4. aide du sous-parseur `validate` (l.289) ;
|
||
5. README bloc de commande « schéma + 12 invariants » (l.65) ;
|
||
6. README titre de section « ## Les 12 invariants » (l.69) ;
|
||
7. README prose de score « 12 invariants de cross-cohérence » (l.105).
|
||
|
||
Ajouter un contrôle `# 13 ·` à `_validate` sans toucher les 7 chaînes ⇒ le README
|
||
ET les self-reports du `.py` mentent en silence pendant que la CI applique 13
|
||
contrôles. Aucune suite `tests/` (qui teste des FONCTIONS de validation, pas la
|
||
prose) n'attrape ce « vert trompeur ».
|
||
|
||
**Particularité du registre fiscal** (≠ SEO/Chat OTOIA au ledger propre 1..N) :
|
||
l'**invariant 1 est la validation de SCHÉMA** (`validate(bundle, schema)`, **non
|
||
marquée** `# 1 ·`), les marqueurs couvrent **2..N**, et l'invariant **12 est
|
||
splitté** en `# 12a ·` (unicité évènements) / `# 12b ·` (comptes du manifeste) —
|
||
deux volets d'un même invariant. Le gate reconstruit donc le ledger = `{1:schéma}`
|
||
∪ marqueurs-top-level, en collapsant 12a/12b, tout en **mordant un vrai doublon**
|
||
d'un numéro NON suffixé.
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, bloc « Fiscal · e-CF DGII · IDENTITÉ
|
||
du COMPTE D'INVARIANTS ») — RECOMPUTE le ledger depuis le SEUL registre-source
|
||
(`# N ·` de `_validate`) et exige :
|
||
- **(a)** ledger CONTIGU 1..N (schéma #1 présent + marqueurs 2..N sans lacune ;
|
||
doublon nu = défaut mordu ; 12a/12b comptés 1×) ;
|
||
- **(b)** toute mention « N invariants » du `.py` == |ledger| ;
|
||
- **(c)** l'annonce du titre README == |ledger| ;
|
||
- **(d)** toute mention « N invariants » du README == |ledger| ;
|
||
- **(e)** l'ÉNUMÉRATION numérotée du README (« 1. … 2. … N. ») elle-même contiguë
|
||
1..N (item ni sauté ni dupliqué).
|
||
|
||
**Vérif** — 5 checks verts sur l'arbre propre (ledger 1..12 = schéma #1 + 11
|
||
marqueurs). **8 morsures** adversariales : M1 self-report `.py` 12→13 (b) · M2
|
||
titre README 12→13 (c+d) · M3 mention README 12→13 (d) · M4 ajout marqueur `# 13 ·`
|
||
⇒ ledger→13 pendant que la prose reste 12 (b+c+d, preuve que l'autorité est le
|
||
CODE, pas la prose) · M5 suppression `# 7 ·` ⇒ gap non-contigu (a) · M6 doublon nu
|
||
`# 5 ·` ⇒ détection de vrai doublon · M7 énumération README 7.→8. ⇒ deux 8/pas de
|
||
7 (e) · M8 suppression `validate(bundle, schema)` ⇒ invariant 1 introuvable. Gates
|
||
re-verts : `check_artifacts` · `check_docs` · `guard_constraints` ·
|
||
`check_ci_integrity` · `check_readme_claims` · `check_regression` tous exit 0.
|
||
|
||
---
|
||
|
||
## Sprint 8 · buffer · Mobile app_config : gate d'IDENTITÉ du COMPTE D'INVARIANTS de `_validate_bundle` (8ᵉ instance)
|
||
|
||
**Contexte** — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention de
|
||
chiffres »). La classe « ledger d'invariants » (recompute le nombre d'invariants CI
|
||
depuis le registre-source numéroté de la fonction de validation, jamais une liste à
|
||
la main) était déjà appliquée à **7 générateurs** : Chat OTOIA · SEO · Fiscal e-CF ·
|
||
Legal CONFOTUR · CRM Dossier Vente · QA Audit 5D · CRM Commissions. Le **8ᵉ**
|
||
générateur portant le même nombre magique restait NON couvert :
|
||
`mobile/app_config/app_config_gen.py`.
|
||
|
||
Le compte **« 12 invariants »** y est transcrit **à la main à QUATRE endroits**,
|
||
aucun gaté, tous invisibles à `check_artifacts` (le `.py`/`.md`/test n'est pas un
|
||
`out/*.json`) :
|
||
1. `app_config_gen.py` message de succès CLI « … schéma + 12 invariants verts. »
|
||
(l.228) ;
|
||
2. `README.md` bloc de commande « schéma + 12 invariants » (l.48) ;
|
||
3. `README.md` prose de récap « Schéma + 12\ninvariants verts » (l.67-68 — le « 12 »
|
||
et le mot séparés par un saut de ligne) ;
|
||
4. `tests/test_mobile_app_config.py` docstring « … + les 12 invariants du CLI »
|
||
(l.4).
|
||
|
||
Ajouter un contrôle `# Invariant 13 ·` à `_validate_bundle` sans toucher les 4
|
||
chaînes ⇒ le README, le self-report CLI ET la docstring de test mentent en silence
|
||
pendant que la CI applique 13 contrôles. Aucune suite `tests/` (qui EXÉCUTE la
|
||
validation mais ne compte jamais la prose) ne l'attrape.
|
||
|
||
**Particularités vs les 7 blocs précédents** (documentées dans l'en-tête du gate) :
|
||
- le registre-source utilise le style **`# Invariant N ·`** (le mot « Invariant »
|
||
précède le numéro) — les blocs ancrés sur le style nu `# N ·` NE le voient PAS ;
|
||
ancrage sur `#\s*Invariant\s+(\d+)…` ;
|
||
- la fonction s'appelle **`_validate_bundle`** (pas `_validate`) — l'ancre `def
|
||
_validate\b` des autres blocs échoue (`\b` échoue avant `_bundle`) ;
|
||
- ce README n'a NI titre « ## Les N invariants » NI énumération numérotée → on gate
|
||
**(a)(b)(b')(d)**, en AJOUTANT la surface **fichier-de-tests (b')**, absente des 7
|
||
blocs précédents (leurs tests ne portaient pas le chiffre).
|
||
|
||
**Gate ajouté** (`ci/check_readme_claims.sh`, bloc « Mobile · app_config · IDENTITÉ
|
||
du COMPTE D'INVARIANTS ») — RECOMPUTE le ledger depuis le SEUL registre-source
|
||
(`# Invariant N ·` de `_validate_bundle`) et exige :
|
||
- **(a)** ledger CONTIGU 1..N (schéma compté à part ; lacune OU doublon nu = défaut
|
||
mordu) ;
|
||
- **(b)** toute mention « N invariants » du `.py` == |ledger| ;
|
||
- **(b')** toute mention « N invariants » du fichier de tests == |ledger| ;
|
||
- **(d)** toute mention « N invariants » du README == |ledger|.
|
||
|
||
**Vérif** — 4 checks verts sur l'arbre propre (ledger 1..12 = 12 marqueurs). **8
|
||
morsures** adversariales : M1 self-report CLI 12→13 (b) · M2 README bloc-commande
|
||
12→13 (d) · M3 README récap (newline-split) 12→13 (d, preuve que `\s+` traverse le
|
||
saut de ligne) · M4 docstring test 12→13 (b', la surface neuve) · M5 ajout marqueur
|
||
`# Invariant 13 ·` ⇒ ledger→13 pendant que les 3 surfaces prose restent 12 (b+b'+d,
|
||
preuve que l'autorité est le CODE, pas la prose) · M6 suppression `# Invariant 7 ·`
|
||
⇒ gap non-contigu (a) + désync prose · M7 doublon nu `# Invariant 5 ·` ⇒ détection
|
||
de vrai doublon · M8 renommage `_validate_bundle` ⇒ registre introuvable. Gates
|
||
re-verts : `check_artifacts` · `check_docs` · `guard_constraints` ·
|
||
`check_ci_integrity` · `check_readme_claims` · `check_regression` tous exit 0.
|
||
|
||
---
|
||
|
||
## Sprint 8 · buffer · Doc QA : documentation du 2ᵉ étage QA (batterie de gates statiques `ci/*.sh`) dans `03_agents/qa/AGENT.md`
|
||
|
||
**Démarche — vérifier AVANT de gater (mémoire `verify-uncovered-before-gating`).**
|
||
La série récente empile des gates anti-dérive ; avant d'en ajouter un 9ᵉ, j'ai
|
||
d'abord cherché une **vraie dérive présente** plutôt qu'un gate spéculatif :
|
||
|
||
1. Scan ledger-vs-prose sur les 12 générateurs à ledger d'invariants. **Faux
|
||
signal** relevé : `fiscal/ecf_dgii` semblait afficher `ledger_markers=10` vs
|
||
`prose=12`. Investigation : le ledger n'est **pas** nu 1..12 — l'invariant #1
|
||
(validation de schéma, `validate(bundle, schema)`) n'est **pas** marqué `# 1 ·`
|
||
par design, et #12 est scindé en `12a`/`12b`. Le gate fiscal (`check_readme_claims.sh`
|
||
l.1908) **synthétise** déjà `ledger = marqueurs{2..12} ∪ {1}` et vérifie
|
||
self-report / titre README / mentions / énumération = 12. **Aucune dérive** ;
|
||
mon compteur grossier ne voyait ni le schéma-#1 ni `12a/12b`.
|
||
2. Sondé les comptes de `03_agents/{crm,qa}/AGENT.md` (« Total CRM 81 (25+31+25) »,
|
||
« 17 contrôles / 5 dimensions », « 8 livrables + 7 métriques ») → **tous gatés**
|
||
(l.681, 871, 2176, 2624, 3671…). Coverage **très dense**, conforme à la mémoire.
|
||
|
||
**Conclusion** : pas de gate à ajouter (ç'aurait été redondant · CLAUDE.md #5).
|
||
|
||
**Trou documentaire réel comblé.** Le titre + le scope de `qa/AGENT.md` promettent
|
||
des « **gates de méta-niveau** », mais le corps ne tabulait que les **4 modules
|
||
Python** d'audit (`audit_5d` · `audit_4big` · `regression` · `acceptance`) — il
|
||
**omettait entièrement** la batterie de **gates statiques `ci/*.sh`**, qui sont
|
||
pourtant l'application méta-niveau littérale (moitié de l'enforcement QA). Un
|
||
lecteur croyait donc QA = 4 modules.
|
||
|
||
**Ajout** — section « Deuxième étage QA : la batterie de gates statiques (`ci/*.sh`) »
|
||
listant les 7 gates courants par **nom + rôle** (`check_ci_integrity`,
|
||
`check_readme_claims`, `check_artifacts`, `check_regression`, `check_docs`,
|
||
`guard_constraints`, `validate_json`). **Anti-dérive by design** : le texte ancre
|
||
l'autorité du périmètre sur `check_ci_integrity` (qui **découvre** l'ensemble via
|
||
`git ls-files 'ci/*.sh'` et refuse tout gate décâblé/orphelin) et présente la liste
|
||
comme *l'état courant*, PAS une constante figée → aucun compte agrégé driftable
|
||
introduit.
|
||
|
||
**Sûreté** — prose sans lien Markdown neuf (check_docs), sans URL git interdite ni
|
||
terme CRM/paiement banni nu ; les lignes citant les prohibitions portent un
|
||
marqueur (`proscrites`/`interdits`/`bannie`) donc `guard_constraints` les laisse
|
||
passer. **Vérif** : 7 gates re-verts (guard_constraints · check_docs ·
|
||
check_readme_claims · check_ci_integrity · check_artifacts · check_regression ·
|
||
validate_json tous exit 0).
|