[DTP-Worker] Sprint 8 · buffer · CRM/DocType porteur : le NOM OTO Dossier Vente — le POINT D'ATTACHE du pipeline vente (le Workflow s'y branche via son document_type) — était RECOPIÉ À LA MAIN à ≥4 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) et ASSERTÉ EN PROSE égal à workflow.document_type, mais AUCUN gate ne couvrait le NOM. Le bloc CRM pipeline ne recompute que le COMPTE d'états (9), aveugle à l'identité ; aucune suite tests/ (FONCTIONS de graphe) n'attrape la prose. Piège : RENOMMER le DocType dans doctype_spec.json reconstruit les DEUX artefacts de façon COHÉRENTE (le document_type du workflow suit, les tests restent verts) mais périme les mentions en prose 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. Gate ajouté (bloc « CRM · IDENTITÉ du DocType porteur ») : nom recomputé de doctype_oto_dossier_vente.json[name] byte-gaté, chaque prose exigée EXACTE + cohérences croisées DocType.name == MANIFEST.doctype_name (interne) & == workflow.document_type (attache porteur↔workflow). 7 morsures vérifiées (nom artefact renommé → 6 lignes en cascade · document_type divergent → attache rompue · doctype_name divergent → incohérence interne · les 4 mentions périmées), restauré vert, 7 gates re-verts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -457,3 +457,59 @@ absolu). Sibling gates re-verts : `check_ci_integrity`/`check_docs`/
|
||||
é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.
|
||||
|
||||
+30
-1
File diff suppressed because one or more lines are too long
@@ -976,6 +976,77 @@ if wf_states is not None:
|
||||
bad(f"CRM pipeline · {DV_RD} — dit états={m.group(1)} MAIS MANIFEST dit "
|
||||
f"états={wf_states} → régénérer le README")
|
||||
|
||||
# ============================================================================
|
||||
# CRM · IDENTITÉ du DocType porteur — le NOM `OTO Dossier Vente`
|
||||
# ----------------------------------------------------------------------------
|
||||
# Le module dossier_vente émet out/doctype_oto_dossier_vente.json dont le champ
|
||||
# `name` EST le nom du DocType porteur (= MANIFEST.doctype_name), byte-gaté par
|
||||
# check_artifacts ⇒ artefact faisant autorité. Ce NOM est le POINT D'ATTACHE du
|
||||
# pipeline : le Workflow vente s'y branche via son `document_type`
|
||||
# (workflow.json[0].document_type). Le nom est RECOPIÉ À LA MAIN à plusieurs
|
||||
# endroits, AUCUN gaté sur le NOM (le bloc CRM pipeline ci-dessus ne recompute
|
||||
# que le COMPTE d'états, aveugle à l'identité) :
|
||||
# • dossier_vente/README.md — titre « # DocType porteur · `NOM` » ET la ligne
|
||||
# « Nom du DocType = `document_type` du workflow (`NOM`) »
|
||||
# • 03_agents/crm/AGENT.md:25 — « **DocType porteur** `NOM` »
|
||||
# • 03_agents/onapi_legal/AGENT.md:50 — « cible = `workflow.document_type` (`NOM`) »
|
||||
# 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
|
||||
# workflow.document_type suit, les tests — qui testent des FONCTIONS — 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 » (crm/
|
||||
# AGENT.md:25) veut prévenir. Aucune suite tests/ (FONCTIONS de graphe, pas la
|
||||
# prose) n'attrape ce « vert trompeur ». On RECOMPUTE le nom depuis l'artefact
|
||||
# (zéro duplication) et on exige que chaque prose le nomme EXACTEMENT (backtické).
|
||||
# Cohérences croisées bonus (mordent un artefact INTERNEMENT incohérent) :
|
||||
# DocType.name == MANIFEST.doctype_name · == workflow.document_type (l'attache
|
||||
# porteur↔workflow). Un claim absent échoue AUSSI (traçabilité #6).
|
||||
# ============================================================================
|
||||
try:
|
||||
dv_dt = load("crm/dossier_vente/out/doctype_oto_dossier_vente.json")
|
||||
dv_man = load("crm/dossier_vente/out/MANIFEST.json")
|
||||
dt_name = dv_dt["name"]
|
||||
dv_man_name = dv_man["doctype_name"]
|
||||
wf_doc_type = load("crm/workflow_vente/out/workflow.json")[0]["document_type"]
|
||||
except (OSError, KeyError, ValueError, IndexError, TypeError) as e:
|
||||
bad(f"CRM DocType porteur · artefact illisible/inattendu : {e}"); dt_name = None
|
||||
if dt_name:
|
||||
# bonus 1 : cohérence interne du module (DocType.name == MANIFEST.doctype_name)
|
||||
if dv_man_name != dt_name:
|
||||
bad(f"CRM DocType porteur · incohérence artefacts : DocType.name "
|
||||
f"« {dt_name} » MAIS MANIFEST.doctype_name « {dv_man_name} » → régénérer out/")
|
||||
else:
|
||||
good(f"CRM DocType porteur · DocType.name == MANIFEST.doctype_name « {dt_name} »")
|
||||
# bonus 2 : l'attache porteur↔workflow — le Workflow s'y branche par document_type
|
||||
if wf_doc_type != dt_name:
|
||||
bad(f"CRM DocType porteur · attache ROMPUE : workflow.document_type "
|
||||
f"« {wf_doc_type} » ≠ DocType porteur « {dt_name} » → le Workflow "
|
||||
f"s'attacherait à un DocType FANTÔME")
|
||||
else:
|
||||
good(f"CRM DocType porteur · attache workflow.document_type == « {dt_name} » "
|
||||
f"(le pipeline a bien où s'attacher)")
|
||||
# prose : chaque doc doit nommer le DocType EXACTEMENT (backtické)
|
||||
esc = re.escape(dt_name)
|
||||
DV_DOC = [
|
||||
(DV_RD, rf"# DocType porteur · `{esc}`", "titre dossier_vente"),
|
||||
(DV_RD, rf"Nom du DocType\*\* = `document_type` du workflow \(`{esc}`\)",
|
||||
"ligne « Nom du DocType »"),
|
||||
("03_agents/crm/AGENT.md", rf"DocType porteur\*\* `{esc}`", "fiche CRM:25"),
|
||||
("03_agents/onapi_legal/AGENT.md",
|
||||
rf"cible = `workflow\.document_type` \(`{esc}`\)", "fiche onapi_legal:50"),
|
||||
]
|
||||
for path, pat, label in DV_DOC:
|
||||
txt = read_norm(path)
|
||||
if txt is None:
|
||||
continue
|
||||
if re.search(pat, txt):
|
||||
good(f"CRM DocType porteur · {label} — nomme « {dt_name} » == artefact")
|
||||
else:
|
||||
bad(f"CRM DocType porteur · {label} ({path}) — nom « {dt_name} » "
|
||||
f"INTROUVABLE/périmé → régénérer la doc")
|
||||
|
||||
# ============================================================================
|
||||
# CRM (2e surface du MÊME README) · ÉNUMÉRATION des transitions à SÉPARATION
|
||||
# DES POUVOIRS — la phrase « engagent de l'argent ou clôturent — confirmer
|
||||
|
||||
Reference in New Issue
Block a user