{
  "summary": "Exposer 6 designs bespoke en blocks sélectionnables (réplique le pilote benefices-mesurables) — fan-out fichiers par design + fusion sérialisée des fichiers partagés",
  "agentCount": 7,
  "logs": [],
  "result": {
    "created": [
      {
        "slug": "preuve-chiffres",
        "layoutSnake": "preuve_chiffres",
        "pascal": "PreuveChiffres",
        "category": "Réassurance",
        "fragmentConst": "PREUVE_CHIFFRES_FRAGMENT",
        "fragmentImport": "./preuveChiffres",
        "fragmentSpread": "PreuveChiffresFields",
        "registryKey": "PreuveChiffresLayout",
        "blockImport": "./PreuveChiffres",
        "blockComponent": "PreuveChiffres",
        "typeInterface": "export interface PreuveChiffresBlock {\n  __typename: string;\n  origine?: string | null;\n  entete?: WpEntete | null;\n  chiffres?:\n    | { icone?: string | null; valeur?: string | null; libelle?: string | null }[]\n    | null;\n  refTitre?: string | null;\n  refIntro?: string | null;\n  logosTitre?: string | null;\n  logos?:\n    | { image?: WpImageField | null; nom?: string | null }[]\n    | null;\n}",
        "filesCreated": [
          "/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/preuve-chiffres.php",
          "/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/preuveChiffres.ts",
          "/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/blocks/PreuveChiffres.tsx"
        ],
        "notes": "Composant bespoke réutilisé : src/components/industrie/IndustrieProof.tsx (fusion IndustrieProof/DistributionProof, 3 stat-cards navy + bande logos). Mapping des props :\n- entete.badge/titre/intro -> badge/titre/intro (titre est ReactNode côté composant ; un string WP est un ReactNode valide, passé tel quel).\n- repeater `chiffres` (icone/valeur/libelle) -> figures[] { icon, value, label } ; icône via iconFor() avec fallback TrendingDown (un des défauts du composant).\n- refTitre/refIntro -> refTitle/refIntro ; logosTitre -> logosTitle.\n- repeater `logos` (image/nom) -> industrialLogos[] { name, url } via mapImage() ; name = altText || nom, url = sourceUrl ; filtre les entrées sans image.\nParité pixel : chaque prop n'est envoyée que si non vide ({...(x ? {prop:x} : {})}), sinon le composant garde ses défauts (defaultFigures, defaultIndustrialLogos, defaultTitre avec son <br/> + span teal, etc.).\nProp `sector` NON exposée au BO : c'est un type union (ReferenceSectorSlug) qui pilote SectorReferenceHighlights ; laissé au défaut 'industrie' (front-controlled, comme demandé « uniquement les champs réellement éditables »).\nSous-champs de repeater en noms simples (icone/valeur/libelle ; image/nom) — scopés au type GraphQL du layout, pas de collision.\ncvb_repeater : aucun sous-champ nommé `titre`, donc pas de collapsed auto (comportement attendu).\nNON modifiés (phase Fusion) : index.ts, types.ts, BlockRenderer.tsx, sections.php. Pas de build lancé.\nÀ coller dans types.ts : l'interface PreuveChiffresBlock fournie (réutilise WpEntete et WpImageField déjà définis dans types.ts)."
      },
      {
        "slug": "cta-final-simple",
        "layoutSnake": "cta_final_simple",
        "pascal": "CtaFinalSimple",
        "category": "Conversion",
        "fragmentConst": "CTA_FINAL_SIMPLE_FRAGMENT",
        "fragmentImport": "./ctaFinalSimple",
        "fragmentSpread": "CtaFinalSimpleFields",
        "registryKey": "CtaFinalSimpleLayout",
        "blockImport": "./CtaFinalSimple",
        "blockComponent": "CtaFinalSimple",
        "typeInterface": "export interface CtaFinalSimpleBlock {\n  __typename: string;\n  origine?: string | null;\n  titre?: string | null;\n  sousTitre?: string | null;\n  bandeauPreuve?: string | null;\n  ctaPrimaire?: WpCta | null;\n  ctaSecondaire?: WpCta | null;\n}",
        "filesCreated": [
          "/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/cta-final-simple.php",
          "/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/ctaFinalSimple.ts",
          "/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/blocks/CtaFinalSimple.tsx"
        ],
        "notes": "Composant bespoke réutilisé : src/components/CTASection.tsx (fond navy #192A3A, blobs teal, titre centré, double CTA pill). Mapping props -> champs WP : title<-titre, subtitle<-sousTitre, proofBanner<-bandeauPreuve, primaryLabel/primaryHref<-ctaPrimaire(label/lien), secondaryLabel/secondaryHref<-ctaSecondaire(label/lien). Parité pixel : chaque prop n'est passée que si non vide (via {...(x ? {prop:x} : {})}) pour laisser les défauts du composant s'appliquer (title/subtitle/labels/hrefs par défaut). Le style de CTA n'est PAS exposé (CTASection a un primaire cta-gradient et un secondaire bordé rouge en dur) — on ne mappe donc que label+lien des deux CTA ; le champ `style` du clone_cta existe côté ACF mais reste inutilisé côté front, ce qui est volontaire (le design est figé). Pas d'icônes ni de repeater dans ce block, donc iconFor() n'est pas utilisé ici (le composant n'a pas de champ icône). Type GraphQL attendu : ${SECTIONS_TYPE_PREFIX}CtaFinalSimpleLayout. Phase Fusion (NON faite ici) : ajouter le require du layout php dans sections.php, importer CTA_FINAL_SIMPLE_FRAGMENT + spread CtaFinalSimpleFields dans l'agrégat de fragments, ajouter CtaFinalSimpleBlock dans types.ts, et brancher CtaFinalSimpleLayout: CtaFinalSimple dans le registry de BlockRenderer.tsx (attention au tri par longueur décroissante des clés). PHP linté sans erreur."
      },
      {
        "slug": "cta-final-enrichi",
        "layoutSnake": "cta_final_enrichi",
        "pascal": "CtaFinalEnrichi",
        "category": "Conversion",
        "fragmentConst": "CTA_FINAL_ENRICHI_FRAGMENT",
        "fragmentImport": "./ctaFinalEnrichi",
        "fragmentSpread": "CtaFinalEnrichiFields",
        "registryKey": "CtaFinalEnrichiLayout",
        "blockImport": "./CtaFinalEnrichi",
        "blockComponent": "CtaFinalEnrichi",
        "typeInterface": "export interface CtaFinalEnrichiBlock {\n  __typename: string;\n  origine?: string | null;\n  eyebrow?: string | null;\n  titre?: string | null;\n  texte?: string | null;\n  ctaPrimaire?: WpCta | null;\n  ctaSecondaire?: WpCta | null;\n  reassurances?:\n    | { icone?: string | null; titre?: string | null }[]\n    | null;\n}",
        "filesCreated": [
          "/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/cta-final-enrichi.php",
          "/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/ctaFinalEnrichi.ts",
          "/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/blocks/CtaFinalEnrichi.tsx"
        ],
        "notes": "Composant bespoke réutilisé : src/components/industrie/IndustrieFinalCTA.tsx (les *FinalCTA fusionnés via ce seul composant). PARITÉ PIXEL : aucune prop n'est passée si vide ({...(x ? {prop:x} : {})}), le composant retombe alors sur ses défauts (eyebrow \"Cool roof industriel\", titre/texte par défaut, CTAs vers /diagnostic, defaultReassurances : 3 pastilles calendar-check / badge-check / award).\n\nMapping CTA : le composant attend ctaPrimaireLabel/ctaPrimaireHref et ctaSecondaireLabel/ctaSecondaireHref (plat). Le block ACF expose des groupes cvb_clone_cta → ctaPrimaire { label lien style } / ctaSecondaire { label lien style }. On mappe label→...Label et lien→...Href ; le sous-champ `style` du clone n'est PAS utilisé (les deux boutons ont un style fixe dans le bespoke), il reste éditable mais ignoré côté front — conserver le clone garde la cohérence avec le reste du SCHEMA.\n\nRéassurances : repeater max 3 (icone + titre simples, scopés au type GraphQL du layout). Mappées en { icon: iconFor(icone) ?? HelpCircle, title: titre }. Fallback parité : si 0 réassurance valide, on ne passe pas la prop → defaultReassurances du composant. Note : les icônes par défaut (calendar-check) ne font PAS partie de la liste blanche cvb_select_icone, donc impossible de reproduire à l'identique les défauts via le BO ; pour la parité exacte laisser le repeater vide.\n\nHeader : volontairement PAS de cvb_clone_entete — le bespoke utilise eyebrow/titre/texte (pas badge/titre/intro du SectionHeading), donc champs directs pour matcher exactement les props du composant.\n\nChoix du nom de type GraphQL : ${SECTIONS_TYPE_PREFIX}CtaFinalEnrichiLayout (PascalCase du layout snake cta_final_enrichi → CtaFinalEnrichi). À confirmer en phase Fusion contre le nom réel généré par WPGraphQL for ACF.\n\nNON TOUCHÉ (phase Fusion) : index.ts, types.ts (coller typeInterface — WpCta déjà importé/défini dans le fichier), BlockRenderer.tsx, sections.php. Pas de build lancé."
      },
      {
        "slug": "expertise",
        "layoutSnake": "expertise",
        "pascal": "Expertise",
        "category": "Grilles & listes",
        "fragmentConst": "EXPERTISE_FRAGMENT",
        "fragmentImport": "./expertise",
        "fragmentSpread": "ExpertiseFields",
        "registryKey": "ExpertiseLayout",
        "blockImport": "./Expertise",
        "blockComponent": "Expertise",
        "typeInterface": "export interface ExpertiseBlock {\n  __typename: string;\n  origine?: string | null;\n  entete?: WpEntete | null;\n  cartes?:\n    | {\n        icone?: string | null;\n        titre?: string | null;\n        texte?: string | null;\n        featured?: boolean | null;\n      }[]\n    | null;\n}",
        "filesCreated": [
          "/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/expertise.php",
          "/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/expertise.ts",
          "/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/blocks/Expertise.tsx"
        ],
        "notes": "Composant bespoke réutilisé tel quel: src/components/ExpertiseSection.tsx (grille 2×2, carte featured = fond navy `bg-foreground`). Champs ACF strictement alignés sur les props du composant: entete{badge,titre,intro} (cvb_clone_entete) → props badge/titre/intro; repeater `cartes` (max 4, comme la grille 2×2) avec sous-champs scopés icone/titre/texte/featured. Le CTA de ExpertiseSection est codé en dur (href=\"/diagnostic\", \"Demander un devis\") SANS prop dédiée → volontairement PAS de cvb_clone_cta dans le layout/fragment (aucun champ mort). Parité pixel: on n'envoie une prop QUE si non vide via {...(x ? {prop:x} : {})}; cards passé uniquement si au moins une carte a un titre. Mapping icône via iconFor() avec fallback HelpCircle (comme le pilote BeneficesMesurables). `featured` mappé en booléen strict via Boolean(c.featured). `titre` du composant est ReactNode mais accepte une string WP plate (pas de parseAccent côté bespoke). Phase Fusion (NON faite ici): brancher EXPERTISE_FRAGMENT dans le registry des fragments, ajouter ExpertiseBlock à types.ts, mapper ExpertiseLayout→Expertise dans index.ts/BlockRenderer.tsx, et enregistrer expertise.php dans sections.php. Vérifier le préfixe GraphQL: fragment sur `${SECTIONS_TYPE_PREFIX}ExpertiseLayout` (SECTIONS_TYPE_PREFIX défaut \"SectionsSections\", override via WP_SECTIONS_TYPE_PREFIX)."
      },
      {
        "slug": "logos-presse",
        "layoutSnake": "logos_presse",
        "pascal": "LogosPresse",
        "category": "Réassurance",
        "fragmentConst": "LOGOS_PRESSE_FRAGMENT",
        "fragmentImport": "./logosPresse",
        "fragmentSpread": "LogosPresseFields",
        "registryKey": "LogosPresseLayout",
        "blockImport": "./LogosPresse",
        "blockComponent": "LogosPresse",
        "typeInterface": "export interface LogosPresseBlock {\n  __typename: string;\n  origine?: string | null;\n  label?: string | null;\n  logos?:\n    | { image?: WpImageField | null; nom?: string | null; lien?: string | null }[]\n    | null;\n}",
        "filesCreated": [
          "/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/logos-presse.php",
          "/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/logosPresse.ts",
          "/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/blocks/LogosPresse.tsx"
        ],
        "notes": "PressSection n'utilise PAS d'icônes lucide : iconFor() n'est donc pas pertinent ici. Le composant bespoke attend outlets: { name: string; logo: string; url?: string }[] où `logo` est une URL string. Les images WP sont donc mappées via mapImage() puis on passe img.sourceUrl à `logo` (et img.altText en fallback de `name`). Parité pixel : on n'envoie `label` que s'il est non vide, et `outlets` que si au moins un logo a une image (sinon PressSection retombe sur ses defaultOutlets statiques). Le repeater `logos` a des sous-champs simples (image/nom/lien) scopés au type GraphQL LogosPresseLayout — pas de collision avec le layout `logos` existant. Le fragment porte sur ${SECTIONS_TYPE_PREFIX}LogosPresseLayout. PAS d'en-tête (cvb_clone_entete) ni de CTA car PressSection n'expose qu'un label + une liste de logos. Phase Fusion restante : ajouter LogosPresseBlock dans types.ts, importer le fragment dans index.ts, et brancher le registry/BlockRenderer + déclarer le layout dans sections.php (catégorie « Réassurance »)."
      },
      {
        "slug": "avantages-produit",
        "layoutSnake": "avantages_produit",
        "pascal": "AvantagesProduit",
        "category": "Produit",
        "fragmentConst": "AVANTAGES_PRODUIT_FRAGMENT",
        "fragmentImport": "./avantagesProduit",
        "fragmentSpread": "AvantagesProduitFields",
        "registryKey": "AvantagesProduitLayout",
        "blockImport": "./AvantagesProduit",
        "blockComponent": "AvantagesProduit",
        "typeInterface": "export interface AvantagesProduitBlock {\n  __typename: string;\n  origine?: string | null;\n  entete?: WpEntete | null;\n  avantages?:\n    | { icone?: string | null; titre?: string | null; description?: string | null }[]\n    | null;\n}",
        "filesCreated": [
          "/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/avantages-produit.php",
          "/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/avantagesProduit.ts",
          "/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/blocks/AvantagesProduit.tsx"
        ],
        "notes": "Le composant bespoke ProductAdvantages fusionne les Cova*Advantages : props badge/titre/intro + advantages[] (icon: LucideIcon, title, desc). Le repeater ACF s'appelle `avantages` (sous-champs icone/titre/description, scopés au type GraphQL AvantagesProduitLayout, pas de collision). Mapping front : description ACF -> prop `desc` ; icone (string lucide kebab) -> iconFor() avec fallback HelpCircle. Parité pixel : on ne passe une prop QUE si non vide via {...(x ? {prop:x} : {})}, sinon les défauts du composant s'appliquent (badge \"Avantages\", titre/intro/6 avantages par défaut). titre/intro sont passés en string brut (le composant accepte ReactNode, pas de parseAccent/AccentTitle ici contrairement à SectionHeading). Prefix GraphQL : ${SECTIONS_TYPE_PREFIX}AvantagesProduitLayout. Phase Fusion à faire séparément : ajouter le require du layout PHP (sections.php), l'interface dans types.ts, le fragment dans index.ts et l'entrée registry dans BlockRenderer.tsx (clé AvantagesProduitLayout -> composant AvantagesProduit). Non buildé comme demandé."
      }
    ],
    "fusion": "All edits applied cleanly. No duplicates, PHP lints clean.\n\nEDITIONS APPLIQUEES (6 blocks: preuve-chiffres, cta-final-simple, cta-final-enrichi, expertise, logos-presse, avantages-produit)\n\n1. /opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/index.ts\n   - 6 imports ajoutes apres BENEFICES_MESURABLES_FRAGMENT\n   - 6 entrees ajoutees dans le tableau SECTIONS_FRAGMENTS (avant .join)\n   - 6 spreads ajoutes dans SECTIONS_FIELD (...PreuveChiffresFields, ...CtaFinalSimpleFields, ...CtaFinalEnrichiFields, ...ExpertiseFields, ...LogosPresseFields, ...AvantagesProduitFields)\n\n2. /opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/types.ts\n   - 6 interfaces collees apres BeneficesMesurablesBlock (PreuveChiffresBlock, CtaFinalSimpleBlock, CtaFinalEnrichiBlock, ExpertiseBlock, LogosPresseBlock, AvantagesProduitBlock). WpEntete/WpImageField/WpCta deja definis dans le fichier, OK.\n\n3. /opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/blocks/BlockRenderer.tsx\n   - 6 imports default ajoutes apres BeneficesMesurables\n   - 6 entrees registry ajoutees (PreuveChiffresLayout, CtaFinalSimpleLayout, CtaFinalEnrichiLayout, ExpertiseLayout, LogosPresseLayout, AvantagesProduitLayout)\n\n4. /opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/sections.php\n   - 6 slugs ajoutes au tableau $files (zone \"Designs bespoke exposes\")\n   - 6 entrees ajoutees dans cvb_layout_categories() (preuve-chiffres/logos-presse=Reassurance, cta-final-simple/cta-final-enrichi=Conversion, expertise=Grilles & listes, avantages-produit=Produit)\n   - 6 slugs ajoutes au tableau $with_thumb\n\nVERIFICATIONS\n- Fichiers crees par la phase precedente tous presents (6 fragments .ts, 6 composants .tsx, 6 layouts .php).\n- Noms d'exports fragments et de fragments GraphQL (spreads) coherents avec les imports/spreads ajoutes.\n- Suffixes de type GraphQL des fragments (...XxxLayout) == cles du registry (matching par suffixe OK).\n- Default exports presents dans les 6 composants.\n- Aucun doublon de cle registry / const fragment / slug.\n- php -l sur sections.php : pas d'erreur de syntaxe.\n\nCONFLITS / INCOHERENCES DETECTES\n\n1. Vignettes manquantes ($with_thumb) — INCOHERENCE A TRAITER. Aucun JPG de vignette n'existe pour les 6 nouveaux blocks dans wp-content/uploads/block-thumbnails/ (verifie : \"NO THUMBS FOUND\"). Le code construit pourtant `acfe_flexible_thumbnail = .../<slug>.jpg` pour tout slug present dans $with_thumb. Resultat : la modale ACFE de selection de block affichera 6 vignettes 404 (image cassee) jusqu'a capture reelle. J'ai ajoute les slugs conformement a la consigne, mais il faut soit lancer le script de capture (scripts/block-thumbnails/) pour generer ces .jpg, soit retirer temporairement ces 6 slugs de $with_thumb. A noter : le commentaire du code dit \"25/26 layouts ont une vignette\" et n'a pas ete mis a jour (purement documentaire).\n\n2. Ordre de matching par suffixe (BlockRenderer) — VERIFIE, pas de collision. registryKeys est trie par longueur decroissante. Les 6 nouvelles cles ne sont prefixe/suffixe d'aucune autre (ex. pas de \"ChiffresLayout\" vs \"PreuveChiffresLayout\" en conflit car le matching est typename.endsWith(key) et le typename complet se termine par le suffixe le plus specifique ; le tri longueur-desc garantit que PreuveChiffresLayout est teste avant ChiffresLayout). OK.\n\n3. Prefixe GraphQL a confirmer cote WPGraphQL for ACF — DEPENDANCE EXTERNE (deja signalee dans les notes de creation pour cta-final-enrichi et expertise). Les fragments supposent `${SECTIONS_TYPE_PREFIX}<Pascal>Layout`. A valider contre le schema reel genere apres rechargement du plugin (non verifiable sans WP en marche). Cote front c'est le pattern standard utilise par les 27 autres blocks, donc coherent.\n\nAucun build lance (conforme a la consigne)."
  }
}