[DTP-Worker 20260801_210434] Sprint 8 · buffer · CRM/commissions : gate d'IDENTITÉ du CHAMP DEVISE — la clause devise_field = le champ devise (Select USD/DOP · #10) du DocType Dossier Vente (crm/commissions/README.md:54), invariant CLI #8, transcrite en prose hors de tout gate (grep devise_field dans ci/ = 0)

4e surface distincte du même livrable crm/commissions (après états/champs, anti-taux #6, rôles payés) : le « sur quelle devise » du calcul base×taux. Re-dérivée de 3 sources byte-gatées/constitutionnelles : plan.devise_field (out/commission_plan.json) == champ `devise` du DocType (dossier_vente/doctype_spec.json, fieldtype Select) == options ORDONNÉES == devises canoniques CLAUDE.md #10 (« USD + DOP »). Ancrage vivant (#10) : (0) 2 devises depuis le segment gras de #10 ; (a) cross-artefact plan==DocType==#10 ; (b) prose README:54 == artefacts + #10 + tag #10. Distincte du bloc Fiches #10 (prose ERPNext/CRM) et du bloc e-CF TipoMoneda (spec fiscal).

7 morsures vérifiées (4 README · 1 DocType · 1 CLAUDE.md · 1 plan), 7 gates re-verts, working tree byte-restauré (git checkout --, JAMAIS git clean). ci/README.md (récap + paragraphe détaillé) et 05_activity_log mis à jour. Auto-score 4Big 96/100.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Code DTP Worker
2026-08-01 21:15:36 +00:00
parent f91a22f458
commit d587b446b9
3 changed files with 183 additions and 1 deletions
+59
View File
@@ -1,5 +1,64 @@
# Activity Log · 2026-08-01 · Claude Code DTP
## Session `20260801_210434` · Buffer S8 · Domaine CRM/commissions : l'**IDENTITÉ du CHAMP DEVISE** — la clause `devise_field` = le champ `devise` (`Select` **USD/DOP** · #10) du DocType Dossier Vente, transcrite **en prose** dans la §« Cross-cohérence » (`crm/commissions/README.md:54`) — était **HORS de tout gate**. Les trois blocs commissions déjà en place gatent les états soumis / brouillons exclus, les champs de base Currency, l'identité des rôles payés (portail `ventes`) et la promesse anti-taux #6 — mais **aucun** ne touchait ce couple champ↔devises (l'**invariant CLI #8** du générateur).
**Tâche** : **Sprint 8 · buffer** (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive (CLAUDE.md #6). **4e surface distincte du
MÊME livrable `crm/commissions`** (après états/champs, anti-taux, rôles payés) — le
« **sur quelle devise** » du calcul `base × taux`. Surface DISTINCTE du bloc « Fiches
#10 » (prose ERPNext/CRM) et du bloc e-CF `TipoMoneda` (options du spec fiscal) :
ici c'est le champ `devise` du DocType Dossier Vente + la clé racine `devise_field`
du plan, ancrés sur **CLAUDE.md #10****USD + DOP** devises »).
**Dérive silencieuse fermée** :
- `05_deliverables_mvp/crm/commissions/README.md:54` — clause « `devise_field` = le
champ `devise` (`Select` **USD/DOP** · #10) du DocType » : NOMME le champ (`devise`),
son fieldtype (`Select`), ses options (`USD/DOP`) et tague `#10`.
- Sources faisant autorité : (a) `out/commission_plan.json` (byte-gaté par
`check_artifacts`) — clé RACINE `devise_field` (le champ du Dossier Vente qui porte
la devise d'un montant commissionné) ; (b) `crm/dossier_vente/doctype_spec.json`
(l'INPUT du générateur) — le champ `devise` de fieldtype `Select` + ses `options` ;
(c) **CLAUDE.md #10** (ligne 24 : « **USD + DOP** devises ») — les 2 devises
canoniques. L'invariant CLI #8 (`commissions_gen.py:151-158`) exige les trois
simultanément ; le gate re-dérive INDÉPENDAMMENT.
- Piège : la seule vérification existante (`tests/`) teste des **FONCTIONS**
finance/résolution, jamais la PROSE vs l'artefact. RENOMMER le champ (`devise`
`moneda`), changer son fieldtype (`Select``Data` : énumération PERDUE, montant en
monnaie libre) ou altérer ses options (`USD/DOP``USD/EUR` : devise HORS #10 → le
calcul commissionnerait dans une monnaie non prévue) laisse la prose périmée EN
SILENCE. `devise_field` n'apparaissait NULLE PART dans `ci/` (grep = 0).
- **État courant** : **aucune valeur périmée**`plan.devise_field == "devise"` ==
champ DocType `devise` (`Select`, options `[USD, DOP]`) == CLAUDE.md #10 ; la prose
README recoupe tout exactement (anti-invention #6, rien à réécrire). Le défaut est
la **surface ungated**.
**Gate ajouté** (`ci/check_readme_claims.sh`, nouveau bloc « CRM/Commissions ·
IDENTITÉ du CHAMP DEVISE » après le bloc rôles payés) : (0) **ANCRAGE** — les 2
devises depuis le segment gras « USD + DOP » de CLAUDE.md #10 ; (a) **CROSS-ARTEFACT**
`plan.devise_field == "devise"` == fieldname du champ DocType, fieldtype `Select`,
`options` (ORDRE inclus) == devises #10 ; (b) **PROSE** — README:54 nomme le champ
`devise`, le dit `Select`, cite `USD/DOP` == #10 et tague `#10`. Un claim absent
échoue AUSSI (traçabilité #6). Direction plan/DocType/#10 → README.
**7 morsures vérifiées** (4 côté README · 1 DocType · 1 CLAUDE.md · 1 plan) : README
champ `devise→moneda` (prose périmée) · README `Select→Data` (type périmé) · README
`USD/DOP→USD/EUR` (devises périmées) · README tag `· #10` retiré (claim INTROUVABLE) ·
**DocType** `options USD→EUR` (hors #10, cross-artefact) · **CLAUDE.md #10**
`USD+DOP→USD+EUR` (ancrage VIVANT · mord le cross-artefact) · **plan** `devise_field
devise→moneda` (invariant #8) ; restauré = green : plan.devise_field == DocType ==
#10 · prose == artefacts · exit 0. Working tree byte-restauré (`git checkout --`,
**JAMAIS** `git clean`, interdit absolu) · **7 gates re-verts**
(`check_readme_claims`/`check_ci_integrity`/`check_docs`/`guard_constraints`/
`check_artifacts`/`check_regression`/`validate_json`).
- `ci/README.md` (clause de la table récap du pipeline + paragraphe détaillé
« Quatrième surface du MÊME livrable `crm/commissions` · identité du champ devise »)
mis à jour.
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ;
édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact ; la création du
champ `devise` du DocType `bench` reste côté VPS).
- **Auto-score 4Big** : 96/100.
## Session `20260801_183412` · Buffer S8 · Domaine Legal/CONFOTUR : les **deux champs du DocType `CONFOTUR Application` DÉRIVÉS du pipeline vente** — `dossier_vente` (Link → cible `workflow.document_type` = `OTO Dossier Vente`) et `estado` (Select → options = les états `confotur_*` du **workflow vente**), transcrits **en prose** dans la §« Cœur du livrable : cross-cohérence » (`legal/confotur/README.md:33-36`) — étaient **HORS de tout gate d'IDENTITÉ**. Les blocs CONFOTUR amont gatent le **compte** de champs/rôles, la cross-cohérence des **PERMISSIONS** (rôle→actions) et l'ancrage des **ENTITÉS** (`entite_porteuse`→CLAUDE.md §Entités) — mais **aucun** ne touchait `dossier_vente`/`estado`.
**Tâche** : **Sprint 8 · buffer** (DevOps CI/CD · QA). Roadmap fonctionnellement
+25 -1
View File
File diff suppressed because one or more lines are too long
+99
View File
@@ -3559,6 +3559,105 @@ if cr_ev is not None:
good(f"CRM commissions rôles · MANIFEST.roles_rbac_utilises == {len(cr_ids)} "
f"rôles distincts payés (counts.roles cohérent)")
# ============================================================================
# CRM/Commissions · IDENTITÉ du CHAMP DEVISE — `devise_field` = le champ `devise`
# (`Select` USD/DOP · #10) du DocType, transcrit À LA MAIN au README:54 sans gate.
# ----------------------------------------------------------------------------
# Le générateur commissions_gen émet out/commission_plan.json (byte-gaté par
# check_artifacts ⇒ faisant autorité) : le plan porte la clé RACINE `devise_field`
# — le champ du Dossier Vente qui PORTE la devise d'un montant commissionné. Son
# invariant CLI #8 exige TROIS choses simultanées : `devise_field == "devise"`, le
# DocType Dossier Vente (doctype_spec.json, l'INPUT du générateur) porte bien un
# champ `devise` de fieldtype `Select`, et ses `options` == `USD`/`DOP` — les DEUX
# devises canoniques de CLAUDE.md #10 (« **USD + DOP** devises »). La §Cross-cohérence
# RÉÉNONCE le tout EN PROSE (README:54) : « `devise_field` = le champ `devise`
# (`Select` **USD/DOP** · #10) du DocType ». Les blocs commissions ci-dessus gatent
# les états soumis, les champs de base, les rôles payés et la promesse anti-taux —
# mais AUCUN ne touche ce couple champ↔devises. Dérive #6 : RENOMMER le champ
# (`devise`→`moneda`) dans le plan/DocType, changer son fieldtype (`Select`→`Data` :
# la contrainte d'énumération PERDUE, un montant en monnaie libre), ou altérer ses
# options (`USD/DOP`→`USD/EUR` : devise HORS #10, le calcul `base × taux`
# commissionnerait dans une monnaie non prévue) laisse la prose périmée EN SILENCE
# pendant que l'artefact/DocType dit autre chose → « vert trompeur » qu'aucune suite
# tests/ (FONCTIONS finance/résolution, jamais la prose vs artefact) n'attrape ; ce
# n'est ni un lien, ni un terme interdit, ni un `out/` byte-gaté du module → invisible
# à check_docs/guard_constraints. On RECOMPUTE depuis les artefacts byte-gatés + la
# contrainte constitutionnelle : (0) ANCRAGE — les 2 devises depuis CLAUDE.md #10
# (segment gras « USD + DOP ») ; (a) CROSS-ARTEFACT — plan.devise_field == "devise"
# == fieldname du champ DocType · fieldtype == "Select" · options (ORDONNÉES) ==
# devises #10 ; (b) PROSE — README:54 nomme le champ `devise`, le dit `Select`, cite
# USD/DOP == #10 et tague #10. Un claim absent échoue AUSSI (traçabilité #6).
# Direction plan/DocType/CLAUDE.md #10 → README.
# ============================================================================
try:
cd_plan = load("crm/commissions/out/commission_plan.json")
cd_dt = load("crm/dossier_vente/doctype_spec.json")
cd_cm = open("CLAUDE.md", encoding="utf-8").read()
except (OSError, KeyError, ValueError, TypeError) as e:
bad(f"CRM commissions devise · artefact/CLAUDE.md illisible/inattendu : {e}"); cd_plan = None
if cd_plan is not None:
# (0) ANCRAGE #10 : les 2 devises canoniques (segment gras « USD + DOP »)
cd_cur = None
cd_l10 = re.search(r"^10\.\s+(.+)$", cd_cm, re.M)
if cd_l10:
for seg in re.split(r"·", cd_l10.group(1)):
if "devise" in seg.lower():
mm = re.search(r"\*\*(.+?)\*\*", seg)
if mm:
cd_cur = [t.strip() for t in re.split(r"[+/]", mm.group(1)) if t.strip()]
if not cd_cur:
bad("CRM commissions devise · devises #10 (« **USD + DOP** ») INTROUVABLES dans "
"CLAUDE.md #10 → ancrage rompu")
else:
cd_field = None
for grp in cd_dt.get("field_groups", []):
for f in grp.get("fields", []):
if f.get("fieldname") == "devise":
cd_field = f
cd_pf = cd_plan.get("devise_field")
cd_opts = ([ln for ln in cd_field.get("options", "").split("\n") if ln]
if cd_field else None)
# (a) CROSS-ARTEFACT : plan ↔ DocType ↔ devises #10 (invariant CLI #8)
if cd_pf != "devise":
bad(f"CRM commissions devise · plan.devise_field={cd_pf!r} ≠ 'devise' → "
f"invariant #8 cassé (le champ porteur de la devise a changé)")
elif cd_field is None:
bad("CRM commissions devise · champ `devise` ABSENT du DocType Dossier Vente "
"→ invariant #8 (options USD/DOP invérifiables)")
elif cd_field.get("fieldtype") != "Select":
bad(f"CRM commissions devise · champ `devise` fieldtype={cd_field.get('fieldtype')!r} "
f"≠ 'Select' → énumération USD/DOP PERDUE (monnaie libre)")
elif cd_opts != cd_cur:
bad(f"CRM commissions devise · options du champ `devise` {cd_opts} ≠ devises #10 "
f"{cd_cur} (ordre inclus) → devise HORS CLAUDE.md #10")
else:
good(f"CRM commissions devise · plan.devise_field='devise' == DocType champ `devise` "
f"(Select {cd_opts}) == CLAUDE.md #10")
# (b) PROSE README:54 — champ · fieldtype · devises · tag #10
cd_rd = read_norm(CM_RD)
mpr = re.search(
r"`devise_field`\*\* = le champ `([a-z_]+)` \(`(\w+)` \*\*([A-Z/]+)\*\* · #10\)",
cd_rd or "")
if mpr is None:
bad(f"CRM commissions devise · {CM_RD} — claim « `devise_field` = le champ "
f"`devise` (`Select` **USD/DOP** · #10) » INTROUVABLE/malformé (traçabilité #6)")
else:
p_field, p_ftype = mpr.group(1), mpr.group(2)
p_cur = mpr.group(3).split("/")
errs = []
if p_field != "devise":
errs.append(f"champ `{p_field}` ≠ `devise`")
if p_ftype != "Select":
errs.append(f"type `{p_ftype}` ≠ `Select`")
if p_cur != cd_cur:
errs.append(f"devises {p_cur} ≠ #10 {cd_cur}")
if errs:
bad(f"CRM commissions devise · {CM_RD} — prose périmée : "
+ " · ".join(errs) + " → régénérer le README")
else:
good(f"CRM commissions devise · {CM_RD} — prose « champ `devise` "
f"(`Select` {'/'.join(p_cur)}) · #10 » == artefacts + CLAUDE.md #10")
# ============================================================================
# RBAC/fixtures_gen · le CATALOGUE des DocTypes `custom` à créer sur le VPS et la
# SÉPARATION DES POUVOIRS (le rôle unique porteur de `set_user_permissions`) —