[DTP-Worker] Sprint 8 · buffer L60 · Domaine CRM/dossier_vente (2e surface) : le CATALOGUE de projets du DocType porteur (« Projet (Select P01..P09 ancré sur CLAUDE.md) ») était ANCRÉ à CLAUDE.md EN PROSE sans AUCUN gate d'IDENTITÉ. Le bloc CRM pipeline existant ne recompute que le COMPTE d'états (« les 9 états du pipeline ») du MÊME README — jamais la liste des projets. Ce catalogue est DATA-DERIVED : le champ Select projet de crm/dossier_vente/out/doctype_oto_dossier_vente.json (byte-gaté par check_artifacts) porte l'ensemble ORDONNÉ des 9 options (P01 Structure … P09 1069 Crisfer), transcription de la section « ## Projets » de CLAUDE.md (source faisant autorité · contraintes #6/#7). PIÈGE : check_artifacts ne prouve QUE DocType==spec (byte-for-byte), et la spec RECOPIE elle-même le catalogue — donc TOUTE la chaîne (spec → DocType) peut DÉRIVER de CLAUDE.md en restant BYTE-VERTE. RENOMMER un projet (P03 Nakua → P03 Nakua Bay) · en AJOUTER un (P10) · en RETIRER un laisse le Select périmé, contredisant silencieusement CLAUDE.md pendant que la prose « ancré sur CLAUDE.md » devient FAUSSE — « vert trompeur » qu'aucune suite tests/ (qui teste des FONCTIONS de builder, pas l'ancrage à CLAUDE.md) n'attrape → nouveau bloc recomputant le catalogue depuis « ## Projets » de CLAUDE.md (zéro duplication · source unique · même esprit que le roadmap-anchor INV11 qui ancre les roadmap_line au fichier roadmap) et exigeant que les options du Select l'égalent EXACTEMENT (set-diff absent ET en trop, PUIS ordre · NFC) + que la prose porte la plage P01..P<dernier> + le mot d'ancrage. Cohérences croisées en bonus (mordent un catalogue CLAUDE.md internement incohérent) : catalogue NON VIDE, codes SANS DOUBLON et CONTIGUS depuis P01. Un claim absent échoue AUSSI. 6 morsures vérifiées : CLAUDE.md rename P03 Nakua→P03 Nakua Bay (Select DÉRIVE : absents=[P03 Nakua Bay] en trop=[P03 Nakua]) · CLAUDE.md +P10 (Select absents=[P10 …] + README dit P01..P09 MAIS catalogue P01..P10) · DocType réordonné (bon ENSEMBLE mais ORDRE ≠) · DocType renomme 1 libellé (P07 Aqua Terra Las Terrenas→P07 Aqua Terra) · README plage P01..P09→P01..P08 · README ancrage supprimé (claim INTROUVABLE) ; restauré = green : Select projet == CLAUDE.md (9 projets, ordre inclus) · plage P01..P09 + ancrage · catalogue contigu · exit 0. État courant : aucun projet périmé (anti-invention #6, rien à réécrire) — le défaut est la surface ungated. ci/README.md (table + détail « catalogue de projets ancré sur CLAUDE.md ») mis à jour · 7 gates re-verts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,65 @@
|
||||
# Activity Log · 2026-07-31 · Claude Code DTP
|
||||
|
||||
## Session `20260731_183124` · Buffer S8 · Domaine CRM/dossier_vente (2ᵉ surface) : le **catalogue de projets** du DocType porteur (« Projet (`Select` **P01..P09** ancré sur CLAUDE.md) ») était **ancré à CLAUDE.md EN PROSE** sans AUCUN gate d'identité — le bloc CRM pipeline existant ne recompute que le COMPTE d'états (« les **9 états** du pipeline ») du même README.
|
||||
|
||||
**Tâche** : **Sprint 8 · buffer L60** (DevOps CI/CD · QA). Roadmap fonctionnellement
|
||||
close ; poursuite de la série anti-dérive (CLAUDE.md #6). Cette session cible une
|
||||
**2ᵉ surface data-derived du README `crm/dossier_vente`** — le champ `Select`
|
||||
`projet` du DocType porteur, dont les 9 options (`P01 Structure` … `P09 1069
|
||||
Crisfer`) sont une transcription de la section « ## Projets » de CLAUDE.md, jamais
|
||||
ANCRÉE par aucun gate.
|
||||
|
||||
**Dérive silencieuse fermée** :
|
||||
- `05_deliverables_mvp/crm/dossier_vente/README.md:46` : « Projet (`Select`
|
||||
**P01..P09** ancré sur CLAUDE.md) » ; `doctype_spec.json` `_comment` : « les
|
||||
Select `projet`/`devise` sont ancrés sur CLAUDE.md … pas inventés ».
|
||||
- Source faisant autorité : la section **« ## Projets » de CLAUDE.md** (contraintes
|
||||
#6/#7 · liste canonique des projets). Le DocType (`out/doctype_oto_dossier_vente.json`,
|
||||
byte-gaté par `check_artifacts`) porte l'ensemble ORDONNÉ des 9 options.
|
||||
- `check_artifacts` ne prouve QUE _DocType == spec_ (byte-for-byte) ; la spec recopie
|
||||
elle-même le catalogue → **toute la chaîne (spec → DocType) peut DÉRIVER de
|
||||
CLAUDE.md en restant byte-verte** : **RENOMMER** un projet (`P03 Nakua` → `P03
|
||||
Nakua Bay`), en **AJOUTER** (`P10`) ou en **RETIRER** un laisse le Select périmé,
|
||||
contredisant silencieusement CLAUDE.md pendant que la prose « ancré sur CLAUDE.md »
|
||||
devient FAUSSE — « vert trompeur » qu'aucune suite `tests/` (qui teste des FONCTIONS
|
||||
de builder, pas l'ancrage à CLAUDE.md) n'attrape.
|
||||
- **État courant** : **aucun projet périmé** — les 9 options recoupent CLAUDE.md
|
||||
exactement (anti-invention #6, rien à réécrire). Le défaut est la **surface ungated**.
|
||||
|
||||
**Nouveau bloc dans `ci/check_readme_claims.sh`** : recompute le catalogue depuis
|
||||
« ## Projets » de CLAUDE.md (zéro duplication · source unique · même esprit que le
|
||||
**roadmap-anchor** INV11) et exige que les options du `Select` `projet` l'égalent
|
||||
**exactement** (set-diff absent ET en trop, **puis ordre** · NFC), plus la plage
|
||||
`P01..P<dernier>` + le mot d'ancrage dans le README. Cohérences croisées en bonus
|
||||
(mordent un catalogue CLAUDE.md internement incohérent) : catalogue **non vide,
|
||||
sans doublon et contigu** depuis `P01`. Un claim absent échoue AUSSI.
|
||||
|
||||
### Preuve (6 morsures vérifiées + restauration verte)
|
||||
|
||||
| Injection | Résultat |
|
||||
|---|---|
|
||||
| CLAUDE.md `P03 Nakua → P03 Nakua Bay` (DocType stale) | ✗ Select DÉRIVE : absents=`[P03 Nakua Bay]` en trop=`[P03 Nakua]` · exit 1 |
|
||||
| CLAUDE.md `+P10` (catalogue grandit) | ✗ Select absents=`[P10 …]` + README dit P01..P09 MAIS catalogue P01..P10 · exit 1 |
|
||||
| DocType Select réordonné (bon ensemble, ordre ≠) | ✗ bon ENSEMBLE mais ORDRE ≠ CLAUDE.md · exit 1 |
|
||||
| DocType renomme 1 libellé (`P07 Aqua Terra Las Terrenas → P07 Aqua Terra`) | ✗ Select DÉRIVE : absents/en trop · exit 1 |
|
||||
| README plage `P01..P09 → P01..P08` | ✗ README dit P01..P08 MAIS catalogue P01..P09 · exit 1 |
|
||||
| README ancrage supprimé (reformulé) | ✗ claim « Projet (`Select` P01..P09 ancré sur CLAUDE.md) » INTROUVABLE · exit 1 |
|
||||
| restauré | ✓ Select `projet` == CLAUDE.md (9 projets, ordre inclus) · plage P01..P09 + ancrage · catalogue contigu · exit 0 |
|
||||
|
||||
- **`ci/README.md`** — surface ajoutée à la table récap du pipeline + paragraphe
|
||||
descriptif (« catalogue de projets ancré sur CLAUDE.md »).
|
||||
|
||||
Suite CI complète re-verte (7 gates) : `guard_constraints` · `validate_json` ·
|
||||
`check_docs` · `check_artifacts` · `check_regression` · `check_ci_integrity` ·
|
||||
`check_readme_claims`.
|
||||
|
||||
**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 `20260731_180119` · Buffer S8 · Domaine SEO (3e surface) : la **carte hreflang** (« carte `alternate` **FR/EN/ES** + `x-default` par page ») était décrite EN PROSE dans le README du module `seo` sans AUCUN gate d'IDENTITÉ — les deux blocs SEO existants gatent le COMPTE de mots-clés (artefact `seo_keywords`) et les `@type` du graphe schema.org (artefact `seo_schema_org`), jamais le jeu de langues hreflang qui vit dans un **troisième** artefact.
|
||||
|
||||
**Tâche** : **Sprint 8 · buffer L75** (DevOps CI/CD · QA). Roadmap fonctionnellement
|
||||
|
||||
+19
-1
@@ -20,7 +20,7 @@ et manuellement (`workflow_dispatch`). Jobs statiques (+ une suite `unittest` pa
|
||||
| `check-artifacts` | `ci/check_artifacts.sh` | Reproductibilité : chaque `out/*.json` versionné == build frais | ✅ oui |
|
||||
| `check-regression` | `ci/check_regression.sh` | Fraîcheur : `qa/regression/out/regression_run.json` (`run`) == run frais + verdict PASS | ✅ oui |
|
||||
| `check-ci-integrity` | `ci/check_ci_integrity.sh` | Intégrité du câblage : `gate.needs` == tous les jobs non-manuels · chaque `ci/*.sh` câblé au gate | ✅ oui |
|
||||
| `check-readme-claims` | `ci/check_readme_claims.sh` | Intégrité des chiffres des docs d'entrée : chaque nombre de `README.md` « État courant », de la fiche QA « Verdict agrégé » **et** de la colonne « Tests » par suite des tables de fiches **et** des agrégats en prose (Total CRM / RBAC / e-CF) **et** des comptes par workspace des portails (cartes/liens/rôles, ×2 docs) **et** des comptes RBAC des 3 volets (table Nb rôles/profil, ligne agrégée apply_plan, ventilation userperm) **et** des comptes de mots-clés SEO (total + fr/en/es, ×2 docs) **et** du pipeline vente CRM (états/transitions, ×2 docs + énumération des transitions à séparation des pouvoirs `{action | allow_self_approval=0}` recomputée de `workflow.json`) **et** de l'app mobile Expo (onglets/rôles/langues/identifiants a_confirmer + énumération ordonnée des onglets recomputée de `role_navigation`, bijection onglet⇔portail) **et** du DocType CONFOTUR (champs/données/sections/rôles/évènements de dépôt, ×2 docs) **et** de l'audit 5D (contrôles/dimensions ×2 docs + ventilation verdict pass/fail/à-confirmer + énumération des open items `control→dimension→propriétaire` recomputée de `audit_report.open_items`) **et** du run-book de déploiement VPS (phases ×2 dans la fiche devops + confirmations préalables count & énumération dans le README) **et** du run-sheet de démo (nombre de modules cités dans le diagramme du README) **et** du montage Chat OTOIA par portail (`Custom Block`/configs runtime/portails, tous = 5 aujourd'hui, ×2 docs) **et** du module de recette `qa/acceptance` (couple livrables de sprint + métriques succès MVP, ×2 docs de module au-delà du README d'entrée) **et** du barème de l'auditeur 4Big (nombre de critères en prose + poids par critère de la table + « generator (N critères) », recomputés de `quality_report.criteria`) **et** de la carte de renormalisation par archétype de l'auditeur 4Big (critères EXCLUS de `data_room`/`parser`/`contract`, recomputés des `checks` par archétype de `quality_report.modules`) **et** du contrat de format de l'e-NCF fiscal (`E + tipoeCF(2) + secuencia(10) = 13` : préfixe/longueurs des segments/longueur totale, ×3 README + fiche `erpnext_backend`, recomputés du bloc `e_ncf` d'`ecf_plan.json` + cohérences croisées `1+tipo+seq==longueur` & `|regex digits|==tipo+seq`) **et** de la cross-cohérence du barème de commissions `crm/commissions` (états brouillon exclus recomputés de `{doc_status=0}` de `workflow.json` + champs de base recomputés de `{base_field}` de `commission_plan.json` + invariant 3 « commissionne seulement des états soumis ») **et** du catalogue RBAC `rbac/fixtures_gen` (DocTypes `custom` à créer énumérés == `MANIFEST.custom_doctypes_a_confirmer` + séparation des pouvoirs : le rôle unique porteur de `set_user_permissions` == `custom_docperm.json`) **et** de la composition du graphe schema.org SEO (les `@type` du `@graph` — `Organization` racine + une `Residence` par projet — recomputés de `seo_schema_org.json` + bijection listing⇔projet & absence d'`offers`) **et** de la carte hreflang SEO (les langues de contenu `alternate` `FR/EN/ES` + `x-default` recomputées de `seo_hreflang.json` + cohérences croisées : carte uniforme sur toutes les pages · `x-default` présent partout · langue par défaut ∈ langues de contenu · `x-default`.href == `canonical` == href de la langue par défaut) == artefact cité (#6) | ✅ oui |
|
||||
| `check-readme-claims` | `ci/check_readme_claims.sh` | Intégrité des chiffres des docs d'entrée : chaque nombre de `README.md` « État courant », de la fiche QA « Verdict agrégé » **et** de la colonne « Tests » par suite des tables de fiches **et** des agrégats en prose (Total CRM / RBAC / e-CF) **et** des comptes par workspace des portails (cartes/liens/rôles, ×2 docs) **et** des comptes RBAC des 3 volets (table Nb rôles/profil, ligne agrégée apply_plan, ventilation userperm) **et** des comptes de mots-clés SEO (total + fr/en/es, ×2 docs) **et** du pipeline vente CRM (états/transitions, ×2 docs + énumération des transitions à séparation des pouvoirs `{action | allow_self_approval=0}` recomputée de `workflow.json`) **et** de l'app mobile Expo (onglets/rôles/langues/identifiants a_confirmer + énumération ordonnée des onglets recomputée de `role_navigation`, bijection onglet⇔portail) **et** du DocType CONFOTUR (champs/données/sections/rôles/évènements de dépôt, ×2 docs) **et** de l'audit 5D (contrôles/dimensions ×2 docs + ventilation verdict pass/fail/à-confirmer + énumération des open items `control→dimension→propriétaire` recomputée de `audit_report.open_items`) **et** du run-book de déploiement VPS (phases ×2 dans la fiche devops + confirmations préalables count & énumération dans le README) **et** du run-sheet de démo (nombre de modules cités dans le diagramme du README) **et** du montage Chat OTOIA par portail (`Custom Block`/configs runtime/portails, tous = 5 aujourd'hui, ×2 docs) **et** du module de recette `qa/acceptance` (couple livrables de sprint + métriques succès MVP, ×2 docs de module au-delà du README d'entrée) **et** du barème de l'auditeur 4Big (nombre de critères en prose + poids par critère de la table + « generator (N critères) », recomputés de `quality_report.criteria`) **et** de la carte de renormalisation par archétype de l'auditeur 4Big (critères EXCLUS de `data_room`/`parser`/`contract`, recomputés des `checks` par archétype de `quality_report.modules`) **et** du contrat de format de l'e-NCF fiscal (`E + tipoeCF(2) + secuencia(10) = 13` : préfixe/longueurs des segments/longueur totale, ×3 README + fiche `erpnext_backend`, recomputés du bloc `e_ncf` d'`ecf_plan.json` + cohérences croisées `1+tipo+seq==longueur` & `|regex digits|==tipo+seq`) **et** de la cross-cohérence du barème de commissions `crm/commissions` (états brouillon exclus recomputés de `{doc_status=0}` de `workflow.json` + champs de base recomputés de `{base_field}` de `commission_plan.json` + invariant 3 « commissionne seulement des états soumis ») **et** du catalogue RBAC `rbac/fixtures_gen` (DocTypes `custom` à créer énumérés == `MANIFEST.custom_doctypes_a_confirmer` + séparation des pouvoirs : le rôle unique porteur de `set_user_permissions` == `custom_docperm.json`) **et** de la composition du graphe schema.org SEO (les `@type` du `@graph` — `Organization` racine + une `Residence` par projet — recomputés de `seo_schema_org.json` + bijection listing⇔projet & absence d'`offers`) **et** de la carte hreflang SEO (les langues de contenu `alternate` `FR/EN/ES` + `x-default` recomputées de `seo_hreflang.json` + cohérences croisées : carte uniforme sur toutes les pages · `x-default` présent partout · langue par défaut ∈ langues de contenu · `x-default`.href == `canonical` == href de la langue par défaut) **et** du catalogue de projets du DocType `crm/dossier_vente` (le Select `projet` — les 9 projets `P01..P09` avec libellés — **ancré sur** la section « ## Projets » de `CLAUDE.md`, recomputé de CLAUDE.md : ensemble + ordre == options du DocType byte-gaté, plage `P01..P09` + mot d'ancrage dans le README, catalogue contigu/sans-doublon en bonus) == artefact cité (#6) | ✅ oui |
|
||||
| `gate` | — | Agrégat vert = gate qualité 4Big franchi | ✅ oui |
|
||||
|
||||
Aucune dépendance réseau/marketplace hors `actions/checkout`. Tout tourne avec
|
||||
@@ -218,6 +218,24 @@ contenu (ni vacante ni étrangère), et pour chaque page `x-default`.href ==
|
||||
défaut = canonical » du README). Un claim supprimé (« carte INTROUVABLE ») échoue
|
||||
**aussi**.
|
||||
|
||||
**Catalogue de projets ancré sur CLAUDE.md** (2ᵉ surface du README
|
||||
`crm/dossier_vente`) : le champ `Select` `projet` du DocType porteur
|
||||
(`out/doctype_oto_dossier_vente.json`, byte-gaté par `check_artifacts`) énumère les
|
||||
**9 projets OTO** (`P01 Structure` … `P09 1069 Crisfer`) que le README résume
|
||||
« Projet (`Select` **P01..P09** ancré sur CLAUDE.md) ». Ce catalogue est une
|
||||
**transcription** : la section « ## Projets » de **CLAUDE.md** est la source
|
||||
faisant autorité (#6/#7), mais `check_artifacts` ne prouve que _DocType == spec_
|
||||
(byte-for-byte) — la spec recopie elle-même la liste, donc toute la chaîne peut
|
||||
**dériver** de CLAUDE.md en restant byte-verte : **renommer** un projet
|
||||
(`P03 Nakua` → `P03 Nakua Bay`), en **ajouter** (`P10`) ou en **retirer** un laisse
|
||||
le Select périmé tandis que la prose « ancré sur CLAUDE.md » devient fausse. Même
|
||||
esprit que le **roadmap-anchor** (INV11 · `roadmap_line` ancré au fichier roadmap) :
|
||||
on recompute le catalogue depuis « ## Projets » de CLAUDE.md et on exige que les
|
||||
options du Select l'égalent **exactement** (set-diff absent ET en trop, **puis
|
||||
ordre**), plus la plage `P01..P<dernier>` + le mot d'ancrage dans le README. En
|
||||
bonus, le catalogue CLAUDE.md est vérifié **non vide, sans doublon et contigu**
|
||||
depuis `P01` (byte-déterminisme du contrat). Un claim supprimé échoue **aussi**.
|
||||
|
||||
Même classe enfin sur le **pipeline vente CRM** : le couple « **9 états · 11
|
||||
transitions** » (titre de section du README du module `crm/workflow_vente`) et le
|
||||
**même** compte d'états « les **9 états** du pipeline » (README du DocType porteur
|
||||
|
||||
@@ -1991,6 +1991,95 @@ if hl_pages:
|
||||
f"{'+x-default' if has_xdef else ''} MAIS carte "
|
||||
f"{sorted(content_langs)}+x-default{detail} → régénérer le README")
|
||||
|
||||
# ============================================================================
|
||||
# CRM/dossier_vente (2e surface) · CATALOGUE DE PROJETS « P01..P09 ancré sur
|
||||
# CLAUDE.md » — le Select `projet` du DocType porteur.
|
||||
# ----------------------------------------------------------------------------
|
||||
# Le bloc CRM pipeline ci-dessus recompute le COMPTE d'états du même README
|
||||
# (« les 9 états du pipeline »). Restait, dans le MÊME README, une surface
|
||||
# data-derived distincte et jamais gatée : la liste des PROJETS. Le champ Select
|
||||
# `projet` du DocType (out/doctype_oto_dossier_vente.json, byte-gaté par
|
||||
# check_artifacts) porte l'ENSEMBLE ORDONNÉ des 9 projets OTO
|
||||
# (« P01 Structure », « P02 Coral del Sur », …). Le README:46 le résume
|
||||
# « Projet (`Select` **P01..P09** ancré sur CLAUDE.md) » et le `_comment` du
|
||||
# doctype_spec.json le déclare « ancré sur CLAUDE.md … pas inventé ». Or ce
|
||||
# catalogue est une TRANSCRIPTION MANUELLE : la section « ## Projets » de
|
||||
# CLAUDE.md est la SOURCE FAISANT AUTORITÉ (contraintes #6/#7 · liste des
|
||||
# projets), mais AUCUN gate ne compare les options du DocType à CLAUDE.md.
|
||||
# check_artifacts ne prouve que DocType == spec (byte-for-byte) ; la spec
|
||||
# elle-même recopie la liste — donc TOUTE la chaîne (spec → DocType) peut
|
||||
# DÉRIVER de CLAUDE.md en restant byte-verte : RENOMMER un projet dans CLAUDE.md
|
||||
# (« P03 Nakua » → « P03 Nakua Bay »), en AJOUTER un (P10) ou en RETIRER un
|
||||
# laisse le Select périmé, contredisant silencieusement CLAUDE.md pendant que la
|
||||
# prose « ancré sur CLAUDE.md » devient FAUSSE — « vert trompeur » qu'aucune
|
||||
# suite tests/ (qui teste des FONCTIONS de builder, pas l'ancrage à CLAUDE.md)
|
||||
# n'attrape. Même esprit que le roadmap-anchor (INV11) qui ancre les
|
||||
# `roadmap_line` de l'artefact au fichier roadmap : ici on ANCRE le catalogue
|
||||
# projet du DocType à CLAUDE.md. On RECOMPUTE le catalogue depuis « ## Projets »
|
||||
# de CLAUDE.md (zéro duplication · source unique) et on exige que les options du
|
||||
# Select l'égalent EXACTEMENT (absent ET en trop via set-diff, puis ORDRE), et
|
||||
# que la prose porte la bonne plage P01..P<dernier> + le mot d'ancrage. Un claim
|
||||
# absent échoue AUSSI. Cohérences croisées en bonus (mordent un catalogue
|
||||
# CLAUDE.md internement incohérent) : catalogue NON VIDE, codes SANS DOUBLON et
|
||||
# CONTIGUS depuis P01 (P01,P02,…,P0N — byte-déterminisme du contrat).
|
||||
# ============================================================================
|
||||
def _nfc(s):
|
||||
return unicodedata.normalize("NFC", s)
|
||||
try:
|
||||
cm_raw = open("CLAUDE.md", encoding="utf-8").read()
|
||||
mcm = re.search(r"^## Projets\s*\n(.+)$", cm_raw, re.M)
|
||||
if mcm is None:
|
||||
raise ValueError("section « ## Projets » INTROUVABLE")
|
||||
# catalogue faisant autorité = tokens « PNN Label » séparés par « · »
|
||||
cat = [_nfc(t.strip()) for t in mcm.group(1).split("·") if t.strip()]
|
||||
cat_codes = [re.match(r"(P\d{2})\b", p).group(1) for p in cat]
|
||||
except (OSError, ValueError, AttributeError) as e:
|
||||
bad(f"CRM projets · CLAUDE.md « ## Projets » illisible/inattendu : {e}"); cat = None
|
||||
if cat is not None:
|
||||
# bonus : catalogue non vide · codes sans doublon · contigus depuis P01
|
||||
contig = cat_codes == [f"P{i:02d}" for i in range(1, len(cat_codes) + 1)]
|
||||
if not cat or len(set(cat_codes)) != len(cat_codes) or not contig:
|
||||
bad(f"CRM projets · catalogue CLAUDE.md dégénéré : codes={cat_codes} "
|
||||
f"(attendu P01..P{len(cat_codes):02d} contigus, sans doublon, non vide)")
|
||||
else:
|
||||
good(f"CRM projets · catalogue CLAUDE.md sain : {len(cat)} projets "
|
||||
f"P01..{cat_codes[-1]} contigus")
|
||||
# 1) ANCRAGE : options du Select `projet` du DocType == catalogue CLAUDE.md
|
||||
try:
|
||||
dv_dt = load("crm/dossier_vente/out/doctype_oto_dossier_vente.json")
|
||||
pf = next(f for f in dv_dt["fields"] if f.get("fieldname") == "projet")
|
||||
dt_opts = [_nfc(o.strip()) for o in pf["options"].split("\n") if o.strip()]
|
||||
except (OSError, KeyError, ValueError, StopIteration) as e:
|
||||
bad(f"CRM projets · DocType Select `projet` illisible/inattendu : {e}"); dt_opts = None
|
||||
if dt_opts is not None:
|
||||
if dt_opts == cat:
|
||||
good(f"CRM projets · Select `projet` du DocType == CLAUDE.md "
|
||||
f"({len(cat)} projets, ordre inclus)")
|
||||
elif set(dt_opts) == set(cat):
|
||||
bad(f"CRM projets · Select `projet` porte le bon ENSEMBLE mais un ORDRE "
|
||||
f"≠ CLAUDE.md : DocType={dt_opts} vs {cat} → régénérer le DocType")
|
||||
else:
|
||||
miss = [p for p in cat if p not in dt_opts]
|
||||
extra = [p for p in dt_opts if p not in cat]
|
||||
detail = (f" · absents={miss}" if miss else "") + \
|
||||
(f" · en trop={extra}" if extra else "")
|
||||
bad(f"CRM projets · Select `projet` du DocType DÉRIVE de CLAUDE.md"
|
||||
f"{detail} → régénérer le DocType (ou aligner CLAUDE.md)")
|
||||
# 2) PROSE : README résume la plage P01..P<dernier> + ancrage CLAUDE.md
|
||||
dv_rd2 = read_norm(f"{D}/crm/dossier_vente/README.md")
|
||||
if dv_rd2 is not None:
|
||||
mp = re.search(r"Projet \(`Select` \*\*(P\d{2})\.\.(P\d{2})\*\* "
|
||||
r"ancré sur CLAUDE\.md\)", dv_rd2)
|
||||
if mp is None:
|
||||
bad(f"CRM projets · {D}/crm/dossier_vente/README.md — claim « Projet "
|
||||
f"(`Select` P01..P{cat_codes[-1][1:]} ancré sur CLAUDE.md) » INTROUVABLE")
|
||||
elif (mp.group(1), mp.group(2)) == (cat_codes[0], cat_codes[-1]):
|
||||
good(f"CRM projets · README plage {mp.group(1)}..{mp.group(2)} "
|
||||
f"+ ancrage CLAUDE.md == catalogue")
|
||||
else:
|
||||
bad(f"CRM projets · README dit {mp.group(1)}..{mp.group(2)} MAIS catalogue "
|
||||
f"CLAUDE.md va de {cat_codes[0]} à {cat_codes[-1]} → régénérer le README")
|
||||
|
||||
sys.exit(1 if FAIL else 0)
|
||||
PY
|
||||
rc=$?
|
||||
|
||||
Reference in New Issue
Block a user