[DTP-Worker] Sprint 8 · buffer · Domaine Portails (2e surface) : le CATALOGUE des DocTypes custom à CRÉER avant import (« CONFOTUR Application, Faisabilité, Publiciste Log ») était énuméré À LA MAIN dans le §« Hand-off → agent ERPNext Backend » du README frontend/portails sans AUCUN gate d'IDENTITÉ. Le bloc portails existant ne gate que les COMPTES par workspace (cartes/liens/rôles, ×2 docs) — jamais ce catalogue de hand-off. Ce catalogue est DATA-DERIVED : doctypes_custom_a_creer de frontend/portails/out/MANIFEST.json (byte-gaté par check_artifacts), lui-même l'UNION des doctypes_custom par workspace (flag custom du contrat rbac_50_roles.json, pour les seuls DocTypes CÂBLÉS dans un portail). C'est un ensemble PROPRE au module (sous-ensemble de celui de rbac/fixtures_gen : API Access, custom mais non câblé, en est ABSENT). PIÈGE : check_artifacts ne prouve QUE MANIFEST==build (byte-for-byte) et le build RECOMPUTE l'union — donc AJOUTER une carte visant un nouveau DocType custom au contrat (le MANIFEST rebâtit le catalogue) · en RENOMMER un · en RETIRER un du câblage laisse la consigne de hand-off périmée pendant que l'artefact dit autre chose : l'agent Backend créerait le mauvais jeu de DocTypes (liens pointant dans le vide — le risque même que la consigne veut prévenir) — « vert trompeur » qu'aucune suite tests/ (qui teste des FONCTIONS de mapping, pas la prose de hand-off) n'attrape → nouveau bloc recomputant le catalogue depuis MANIFEST (zéro duplication du contrat du générateur #6 · même patron que le catalogue custom de rbac/fixtures_gen) et exigeant que la prose l'énumère EXACTEMENT (absent ET en trop via set-diff · accents/casse normalisés via unicodedata). Cohérences croisées en bonus (mordent un MANIFEST INTERNEMENT incohérent) : catalogue NON VIDE, sans doublon et TRIÉ (byte-déterminisme) · égal à l'UNION des doctypes_custom par workspace (pas de DocType custom câblé mais oublié du hand-off, ni l'inverse). Un claim absent échoue AUSSI. 5 morsures vérifiées : prose renomme Faisabilité→Faisabilite2 (absents=[faisabilite] en trop=[faisabilite2]) · prose retire Publiciste Log (sous-ensemble) · énumération supprimée (INTROUVABLE) · MANIFEST retire du catalogue un DocType encore câblé (incohérence interne : câblés hors catalogue=[Publiciste Log] + prose en trop) · catalogue MANIFEST non trié (byte-déterminisme) ; restauré = green : [CONFOTUR Application,Faisabilité,Publiciste Log] == MANIFEST == union des workspaces · exit 0. État courant : aucun catalogue périmé (anti-invention #6, rien à réécrire) — le défaut est la surface ungated. ci/README.md (table + détail « 2e surface Portails ») mis à jour · 7 gates re-verts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+22
-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) **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 |
|
||||
| `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) **et** du catalogue des DocTypes `custom` à créer du portail `frontend/portails` (2e surface : le §hand-off VPS énumère `CONFOTUR Application`/`Faisabilité`/`Publiciste Log` == `MANIFEST.doctypes_custom_a_creer` recomputé, set-diff normalisé + cohérences croisées : catalogue trié/sans-doublon/non-vide · égal à l'UNION des `doctypes_custom` par workspace) == 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
|
||||
@@ -497,6 +497,27 @@ Faisabilite2`) · sous-ensemble (retrait `API Access`) · rôle nommé faux (`RB
|
||||
dans l'artefact (singleton cassé) · catalogue MANIFEST non trié (byte-déterminisme) ;
|
||||
restauré = vert.
|
||||
|
||||
Même classe enfin sur une **2ᵉ surface du README des portails** (`frontend/portails`) —
|
||||
le §« Hand-off → agent ERPNext Backend » énumère **à la main** les DocTypes `custom` à
|
||||
créer avant import « (`CONFOTUR Application`, `Faisabilité`, `Publiciste Log`) », un
|
||||
catalogue DATA-DERIVED == `doctypes_custom_a_creer` d'`out/MANIFEST.json` (l'UNION des
|
||||
`doctypes_custom` par workspace, eux-mêmes lus du flag `custom` du contrat RBAC pour les
|
||||
seuls DocTypes **câblés** dans un portail — c'est donc un ensemble PROPRE au module,
|
||||
sous-ensemble de celui de `rbac/fixtures_gen` : `API Access`, custom mais non câblé, en
|
||||
est ABSENT). Le bloc portails ci-dessus ne gate que les **comptes** par workspace
|
||||
(cartes/liens/rôles) — jamais ce catalogue de hand-off. AJOUTER une carte visant un
|
||||
nouveau DocType custom au contrat, en RENOMMER un ou en RETIRER un du câblage laisse la
|
||||
consigne périmée (l'agent Backend créerait le mauvais jeu de DocTypes → liens pointant
|
||||
dans le vide, le risque même que la consigne prévient) — dérive qu'aucune suite `tests/`
|
||||
n'attrape. On RECOMPUTE le catalogue depuis `MANIFEST` et on exige que la prose l'énumère
|
||||
**exactement** (set-diff · accents/casse normalisés). Cohérences croisées en bonus :
|
||||
catalogue **non vide, sans doublon et trié** (byte-déterminisme) · **égal à l'UNION** des
|
||||
`doctypes_custom` par workspace (pas de DocType custom câblé mais oublié du hand-off, ni
|
||||
l'inverse). 5 morsures vérifiées : prose renomme `Faisabilité → Faisabilite2` · prose
|
||||
retire `Publiciste Log` (sous-ensemble) · énumération supprimée (INTROUVABLE) · MANIFEST
|
||||
retire du catalogue un DocType encore câblé (incohérence interne : câblé hors catalogue) ·
|
||||
catalogue MANIFEST non trié (byte-déterminisme) ; restauré = vert.
|
||||
|
||||
Un claim **absent** échoue aussi (la dérive de formulation qui ferait
|
||||
disparaître un chiffre est elle-même une régression de traçabilité). stdlib pur
|
||||
(bash/git/python3), zéro réseau.
|
||||
|
||||
@@ -2080,6 +2080,88 @@ if cat is not None:
|
||||
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")
|
||||
|
||||
# ============================================================================
|
||||
# Portails frontend (2e surface) · CATALOGUE des DocTypes `custom` à CRÉER avant
|
||||
# import — la consigne de hand-off VPS énumérée À LA MAIN, sans gate d'IDENTITÉ.
|
||||
# ----------------------------------------------------------------------------
|
||||
# Le bloc portails ci-dessus recompute les COMPTES par workspace (cartes/liens/
|
||||
# rôles) et la table portail⇔workspace. Restait, dans le MÊME README, une surface
|
||||
# data-derived DISTINCTE et jamais gatée : le §« Hand-off → agent ERPNext Backend »
|
||||
# item 2 énumère « DocTypes custom à créer avant import : `CONFOTUR Application`,
|
||||
# `Faisabilité`, `Publiciste Log` (… le reste est natif v15) ». Ce catalogue est
|
||||
# DATA-DERIVED : c'est out/MANIFEST.json `doctypes_custom_a_creer` (byte-gaté par
|
||||
# check_artifacts) = l'UNION des `doctypes_custom` par workspace, eux-mêmes lus du
|
||||
# flag `custom` du contrat rbac_50_roles.json pour les seuls DocTypes visés par une
|
||||
# carte/un lien du portail. C'est un ensemble PROPRE au module portails (sous-
|
||||
# ensemble de celui de rbac/fixtures_gen : ici seuls comptent les DocTypes custom
|
||||
# effectivement CÂBLÉS dans un workspace — API Access, custom mais non câblé, en est
|
||||
# ABSENT). check_artifacts ne prouve QUE MANIFEST==build (byte-for-byte), et le
|
||||
# build RECOMPUTE l'union — donc AJOUTER une carte visant un nouveau DocType custom
|
||||
# au contrat (le MANIFEST rebâtit le catalogue) · en RENOMMER un · en RETIRER un du
|
||||
# câblage laisse la prose de hand-off périmée pendant que l'artefact dit autre
|
||||
# chose : l'agent Backend créerait le mauvais jeu de DocTypes (liens pointant dans
|
||||
# le vide — le risque même que la consigne veut prévenir) — « vert trompeur »
|
||||
# qu'aucune suite tests/ (qui teste des FONCTIONS de mapping, pas la prose de hand-
|
||||
# off) n'attrape. Même patron que le catalogue custom de rbac/fixtures_gen. On
|
||||
# RECOMPUTE le catalogue depuis MANIFEST (zéro duplication du contrat du générateur
|
||||
# #6) et on exige que la prose l'énumère EXACTEMENT (absent ET en trop via set-diff ·
|
||||
# accents/casse normalisés). Cohérences croisées en bonus (mordent un MANIFEST
|
||||
# INTERNEMENT incohérent) : catalogue NON VIDE, sans doublon et TRIÉ (byte-
|
||||
# déterminisme) · top-level == UNION des `doctypes_custom` par workspace (pas de
|
||||
# DocType custom câblé mais oublié du catalogue de hand-off, ni l'inverse). Un claim
|
||||
# absent échoue AUSSI (traçabilité).
|
||||
# ============================================================================
|
||||
PC_RD = f"{D}/frontend/portails/README.md"
|
||||
def _pc_acc(s):
|
||||
return "".join(c for c in unicodedata.normalize("NFKD", s)
|
||||
if unicodedata.category(c) != "Mn").strip().lower()
|
||||
try:
|
||||
pc_man = load("frontend/portails/out/MANIFEST.json")
|
||||
pc_cat = pc_man["doctypes_custom_a_creer"]
|
||||
pc_union = sorted({d for w in pc_man["workspaces"]
|
||||
for d in w.get("doctypes_custom", [])})
|
||||
except (OSError, KeyError, ValueError, TypeError) as e:
|
||||
bad(f"Portails catalogue · MANIFEST illisible/inattendu : {e}"); pc_cat = None
|
||||
if pc_cat is not None:
|
||||
# bonus : le catalogue est un vrai ENSEMBLE trié non vide (byte-déterminisme)
|
||||
if not pc_cat:
|
||||
bad("Portails catalogue · doctypes_custom_a_creer VIDE → aucun DocType custom "
|
||||
"câblé dans un workspace ? (contrat/spec suspect)")
|
||||
elif pc_cat != sorted(set(pc_cat)):
|
||||
bad(f"Portails catalogue · doctypes_custom_a_creer {pc_cat} non trié/dédupliqué "
|
||||
f"→ générateur non déterministe (attendu {sorted(set(pc_cat))})")
|
||||
else:
|
||||
good(f"Portails catalogue · custom à créer {pc_cat} — ensemble trié, sans doublon")
|
||||
# bonus : cohérence interne — catalogue == union des doctypes_custom par workspace
|
||||
if sorted(set(pc_cat)) != pc_union:
|
||||
miss = sorted(set(pc_union) - set(pc_cat)); extra = sorted(set(pc_cat) - set(pc_union))
|
||||
detail = (f" · câblés hors catalogue={miss}" if miss else "") + \
|
||||
(f" · au catalogue mais non câblés={extra}" if extra else "")
|
||||
bad(f"Portails catalogue · doctypes_custom_a_creer {sorted(pc_cat)} ≠ UNION des "
|
||||
f"doctypes_custom par workspace {pc_union}{detail} → MANIFEST incohérent")
|
||||
else:
|
||||
good(f"Portails catalogue · catalogue == union des doctypes_custom par workspace "
|
||||
f"({pc_union})")
|
||||
pc_rd = read_norm(PC_RD)
|
||||
if pc_rd is not None:
|
||||
# README §Hand-off item 2 — énumération du catalogue custom avant « (issus »
|
||||
mp = re.search(r"custom à créer\*\* avant import[^:]*?: (.+?)\(issus", pc_rd)
|
||||
if mp is None:
|
||||
bad(f"Portails catalogue · {PC_RD} — énumération « DocTypes custom à créer … » "
|
||||
f"INTROUVABLE (attendu {sorted(pc_cat)})")
|
||||
else:
|
||||
got = {_pc_acc(t) for t in re.findall(r"`([^`]+)`", mp.group(1))}
|
||||
exp = {_pc_acc(x) for x in pc_cat}
|
||||
if got == exp:
|
||||
good(f"Portails catalogue · {PC_RD} — hand-off {sorted(pc_cat)} == "
|
||||
f"MANIFEST.doctypes_custom_a_creer")
|
||||
else:
|
||||
miss = sorted(exp - got); extra = sorted(got - exp)
|
||||
detail = (f" · absents={miss}" if miss else "") + \
|
||||
(f" · en trop={extra}" if extra else "")
|
||||
bad(f"Portails catalogue · {PC_RD} — hand-off énumère {sorted(got)} MAIS "
|
||||
f"MANIFEST {sorted(exp)}{detail} → régénérer le README")
|
||||
|
||||
sys.exit(1 if FAIL else 0)
|
||||
PY
|
||||
rc=$?
|
||||
|
||||
Reference in New Issue
Block a user