{
  "summary": "Exposer 4 héros bespoke (product, industrie, secteur, local) — réplique le pilote, champs riches (eyebrow/titre/image/accent/CTAs/stats/badges), noms uniques",
  "agentCount": 5,
  "logs": [],
  "result": {
    "created": [
      {
        "slug": "product-hero",
        "layoutSnake": "product_hero",
        "pascal": "ProductHero",
        "category": "En-têtes",
        "fragmentConst": "PRODUCT_HERO_FRAGMENT",
        "fragmentImport": "import { PRODUCT_HERO_FRAGMENT } from \"./productHero\";",
        "fragmentSpread": "${PRODUCT_HERO_FRAGMENT}",
        "registryKey": "ProductHeroLayout",
        "blockImport": "import ProductHero from \"@/components/blocks/ProductHero\";",
        "blockComponent": "ProductHero",
        "customFieldNames": [
          "tag",
          "product_name",
          "tagline",
          "description",
          "hero_image",
          "accent_color",
          "product_badges",
          "product_stats"
        ],
        "typeInterface": "export interface ProductHeroBlock {\n  __typename: string;\n  origine?: string | null;\n  tag?: string | null;\n  productName?: string | null;\n  tagline?: string | null;\n  description?: string | null;\n  heroImage?: WpImageField | null;\n  accentColor?: string | null;\n  ctaPrimaire?: WpCta | null;\n  ctaSecondaire?: WpCta | null;\n  productBadges?: { texte?: string | null }[] | null;\n  productStats?: { value?: string | null; label?: string | null }[] | null;\n}",
        "filesCreated": [
          "/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/product-hero.php",
          "/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/productHero.ts",
          "/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/blocks/ProductHero.tsx"
        ],
        "notes": "Pattern pilote répliqué pour le héros « Héros produit » (slug product-hero), catégorie En-têtes.\n\n3 fichiers créés (aucun fichier partagé touché ; index.ts/types.ts/BlockRenderer.tsx/sections.php intacts ; pas de build) :\n- layouts/product-hero.php : layout ACF, $l='product_hero', label 'Héros produit'. Champs réellement éditables ↔ props ProductHero.\n- fragments/productHero.ts : PRODUCT_HERO_FRAGMENT sur ${SECTIONS_TYPE_PREFIX}ProductHeroLayout (registryKey = ProductHeroLayout).\n- components/blocks/ProductHero.tsx : importe le composant bespoke @/components/product/ProductHero, mappe avec fallback {...(x?{p:x}:{})}.\n\nAnti-collision WPGraphQL-ACF respectée :\n- repeaters CUSTOM préfixés : product_badges (→ productBadges) et product_stats (→ productStats) au lieu de badges/stats/items génériques.\n- EXCEPTION sûre non préfixée : cvb_clone_cta($l,'cta_primaire'/'cta_secondaire') → ctaPrimaire/ctaSecondaire.\n- sous-champs scalaires laissés génériques (texte, value, label) — OK.\n\nMapping des champs (snake → camel via cvb_camel + graphql_field_name) :\ntag, product_name→productName, tagline, description, hero_image→heroImage (cvb_image, IMAGE_FIELDS), accent_color→accentColor (champ texte hex, instructions + placeholder #038d92), ctaPrimaire/ctaSecondaire (CTA_FIELDS → mappés en {label, href}), product_badges[].texte→inlineBadges[], product_stats[]{value,label}→heroStats[] (max 3, layout table).\n\nNote iconFor() : le composant ProductHero n'utilise aucune icône lucide (badges = texte simple, stats = value/label, accent = barre colorée). iconFor() de @/components/blocks/shared.tsx importé inutilement aurait été du code mort → non importé. Aucun champ icône dans le layout.\n\nheroImage est un <img src> string côté composant : mapImage(...).sourceUrl ?? \"\". heroImageAlt depuis altText si présent.\n\nType ProductHeroBlock inliné dans le block (types.ts non modifié comme demandé). Dépend de WpImageField et WpCta (déjà exportés depuis @/lib/wp/types).\n\nTypecheck (npx tsc --noEmit) : aucune erreur liée à ProductHero.\n\nÀ CÂBLER ensuite par celui qui gère les fichiers partagés (hors périmètre de cette tâche) :\n- fragments/index.ts : exporter PRODUCT_HERO_FRAGMENT + l'inclure dans la query agrégée + le mapping ...PRODUCT_HERO_FRAGMENT sous le bon spread de l'union sections.\n- BlockRenderer.tsx : enregistrer la clé du registry → composant ProductHero (categorie En-têtes).\n- types.ts : si l'union centrale des blocks doit lister ProductHeroBlock, l'ajouter (le type est déjà disponible, inliné dans le block).\n- sections.php : rien à faire si le loader charge automatiquement layouts/*.php ; sinon référencer product-hero.php."
      },
      {
        "slug": "industrie-hero",
        "layoutSnake": "industrie_hero",
        "pascal": "IndustrieHero",
        "category": "En-têtes",
        "fragmentConst": "INDUSTRIE_HERO_FRAGMENT",
        "fragmentImport": "import { INDUSTRIE_HERO_FRAGMENT } from \"./industrieHero\";",
        "fragmentSpread": "...IndustrieHeroFields",
        "registryKey": "IndustrieHeroLayout",
        "blockImport": "import IndustrieHero from \"@/components/blocks/IndustrieHero\";",
        "blockComponent": "IndustrieHero",
        "customFieldNames": [
          "eyebrow",
          "titre",
          "lead",
          "image",
          "badge_titre",
          "industrie_hero_stats",
          "cta_primaire",
          "cta_secondaire"
        ],
        "typeInterface": "export interface IndustrieHeroBlock {\n  __typename: string;\n  origine?: string | null;\n  eyebrow?: string | null;\n  titre?: string | null;\n  lead?: string | null;\n  image?: WpImageField | null;\n  badgeTitre?: string | null;\n  industrieHeroStats?:\n    | { icone?: string | null; value?: string | null; label?: string | null }[]\n    | 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/industrie-hero.php",
          "/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/industrieHero.ts",
          "/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/blocks/IndustrieHero.tsx"
        ],
        "notes": "Héros hub fond clair exposé comme block sélectionnable en répliquant le pattern pilote (benefices-mesurables / cta-final-enrichi). 3 fichiers créés, aucun fichier partagé touché (index.ts/types.ts/BlockRenderer.tsx/sections.php intacts), pas de build.\n\nCHAMPS RÉELLEMENT ÉDITABLES ↔ props IndustrieHero :\n- eyebrow (text) → eyebrow\n- titre (text, *accent*) → titre (le composant accepte ReactNode ; string valide ; défaut JSX si vide)\n- lead (textarea) → lead\n- image (cvb_image array) → imageSrc + imageAlt (via mapImage)\n- badge_titre (text, scalaire) → badgeTitle (badge unique du composant, pas un repeater)\n- industrie_hero_stats (repeater max 4 : icone/value/label) → proofs (barre de preuves), mappé avec iconFor() + fallback HelpCircle\n- cta_primaire / cta_secondaire (cvb_clone_cta) → ctaPrimaireLabel/Href + ctaSecondaireLabel/Href\n\nANTI-COLLISION WPGraphQL-ACF respectée : le repeater proofs est nommé 'industrie_hero_stats' (PRÉFIXÉ) car 'stats' existe déjà sur le layout hero avec une forme différente {value,label} (sans icône). camelCase GraphQL = industrieHeroStats. CTAs non préfixés (exception sûre cvb_clone_cta : ctaPrimaire/ctaSecondaire). badge_titre = sous-champ scalaire → camelCase badgeTitre.\n\nNON EXPOSÉ (piloté par défaut côté front, données front) : cartes/secteurs flottants et le lien « Voir les témoignages » du badge.\n\nÀ CÂBLER MANUELLEMENT (hors périmètre, fichiers partagés interdits) — par quelqu'un d'autre :\n1. types.ts : ajouter l'interface IndustrieHeroBlock (fournie ci-dessus).\n2. fragments/index.ts : importer INDUSTRIE_HERO_FRAGMENT + ajouter ...IndustrieHeroFields à la query agrégée.\n3. BlockRenderer.tsx : mapper le suffixe 'IndustrieHeroLayout' → composant blocks/IndustrieHero.\n4. sections.php : require du layout layouts/industrie-hero.php dans le flexible content + catégorie « En-têtes ».\n5. registryKey = IndustrieHeroLayout (suffixe Pascal — jamais le snake).\n\nNote import block : le composant bespoke s'appelle IndustrieHero ; le block wrapper a été nommé en interne IndustrieHeroBlockComponent pour éviter la collision d'identifiant dans le même module, mais l'export default reste 'IndustrieHero' (import IndustrieHero from \"@/components/blocks/IndustrieHero\")."
      },
      {
        "slug": "secteur-hero",
        "layoutSnake": "secteur_hero",
        "pascal": "SecteurHero",
        "category": "En-têtes",
        "fragmentConst": "SECTEUR_HERO_FRAGMENT",
        "fragmentImport": "import { SECTEUR_HERO_FRAGMENT } from \"./fragments/secteurHero\";",
        "fragmentSpread": "${SECTEUR_HERO_FRAGMENT}",
        "registryKey": "SecteurHeroLayout",
        "blockImport": "import SecteurHero from \"@/components/blocks/SecteurHero\";",
        "blockComponent": "SecteurHero",
        "typeInterface": "export interface SecteurHeroBlock {\n  __typename: string;\n  origine?: string | null;\n  eyebrow?: string | null;\n  titre?: string | null;\n  lead?: string | null;\n  image?: WpImageField | null;\n  ctaLabel?: string | null;\n  secteurStats?:\n    | { icone?: string | null; value?: string | null; label?: string | null }[]\n    | null;\n  secteurLogos?:\n    | { name?: string | null; logo?: WpImageField | null }[]\n    | null;\n  logosTitle?: string | null;\n}",
        "customFieldNames": [
          "secteur_stats",
          "secteur_logos"
        ],
        "filesCreated": [
          "/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/secteur-hero.php",
          "/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/secteurHero.ts",
          "/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/blocks/SecteurHero.tsx"
        ],
        "notes": "3 fichiers créés, aucun fichier partagé touché (index.ts/types.ts/BlockRenderer.tsx/sections.php non modifiés ; pas de build).\n\nComposant réutilisé tel quel : src/components/industries/DistributionHero.tsx (héros navy fusion Distribution/Logistique avec carte lead-form + logos + stats). Le formulaire interne (select secteur / surface / email + estimation) reste 100% non éditable côté WP.\n\nChamps exposés ↔ props DistributionHero :\n- eyebrow → eyebrow\n- titre → titre\n- lead → lead\n- image (return_format array) → imageSrc (sourceUrl) + imageAlt (altText)\n- cta_label → ctaLabel (libellé du bouton uniquement)\n- secteur_stats[] {icone, value, label} → stats[] {icon: iconFor(icone) ?? HelpCircle, value, label}\n- secteur_logos[] {name, logo(image)} → logos[] {name, url: mapImage(logo).sourceUrl}, filtrés sur url non vide\n- logos_title → logosTitle\n\nAnti-collision WPGraphQL-ACF respectée : repeaters préfixés secteur_stats / secteur_logos (PAS stats/logos/items génériques). Sous-champs scalaires laissés simples (value/label/name/icone/logo). camelCase GraphQL résultant : secteurStats, secteurLogos.\n\nFallback systématique {...(x ? {p:x} : {})} → si un champ WP est vide, le défaut hardcodé du composant s'applique (parité pixel préservée).\n\nCâblage restant à faire par le parent (fichiers partagés, hors périmètre de cet agent) :\n1. types.ts : ajouter l'interface SecteurHeroBlock (fournie dans typeInterface ; utilise WpImageField déjà importé).\n2. fragments/index.ts (ou agrégateur) : importer + spread SECTEUR_HERO_FRAGMENT dans la query sections.\n3. BlockRenderer.tsx : mapper la clé registry \"SecteurHeroLayout\" → composant SecteurHero (catégorie « En-têtes »).\n4. sections.php : référencer le layout layouts/secteur-hero.php dans le flexible content + l'attribuer à la catégorie « En-têtes ».\n\nIMPORTANT : registryKey = \"SecteurHeroLayout\" (suffixe Pascal du type GraphQL ${SECTIONS_TYPE_PREFIX}SecteurHeroLayout), jamais le snake secteur_hero."
      },
      {
        "slug": "local-hero",
        "layoutSnake": "local_hero",
        "pascal": "LocalHero",
        "category": "En-têtes",
        "fragmentConst": "LOCAL_HERO_FRAGMENT",
        "fragmentImport": "import { LOCAL_HERO_FRAGMENT } from \"./localHero\";",
        "fragmentSpread": "${LOCAL_HERO_FRAGMENT}",
        "registryKey": "LocalHeroLayout",
        "blockImport": "import LocalHero from \"@/components/blocks/LocalHero\";",
        "blockComponent": "LocalHero",
        "typeInterface": "export interface LocalHeroBlock {\n  origine?: string | null;\n  titre?: string | null;\n  lead?: string | null;\n  ctaPrimaire?: WpCta | null;\n  ctaSecondaire?: WpCta | null;\n}",
        "customFieldNames": [
          "titre",
          "lead",
          "ctaPrimaire",
          "ctaSecondaire"
        ],
        "filesCreated": [
          "/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/local-hero.php",
          "/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/localHero.ts",
          "/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/blocks/LocalHero.tsx"
        ],
        "notes": "Le composant bespoke LocalHero (src/components/LocalHero.tsx) hardcode l'image de fond (heroImage) et le marquee de logos clients — ce ne sont PAS des props. Conformément à la règle \"UNIQUEMENT champs réellement éditables ↔ props\" + \"exactement 3 fichiers / ne pas toucher le composant bespoke\", l'image et les logos NE sont PAS exposés au BO. Seuls titre/lead/ctaPrimaire/ctaSecondaire sont éditables. Anti-collision: aucun repeater/group custom — uniquement les clones CTA (exception sûre, non préfixée). iconFor() non utilisé (pas d'icône dans ce héros). Les props du composant LocalHero sont toutes REQUISES (title, subtitle, primaryCTA{label,href}, secondaryCTA{label,href}); pour respecter le typage sans éditer le bespoke, le block fournit des DEFAULTS de parité pixel sous le spread, puis surcharge avec le pattern {...(x?{p:x}:{})}. CTA mappés via helper local: {label,href} construit depuis WpCta{label,lien} seulement si label ET lien présents. tsc --noEmit : aucune erreur liée à LocalHero. Catégorie cible 'En-têtes' (clé 'local-hero' à ajouter dans cvb_layout_categories de sections.php par l'orchestrateur — NON édité ici comme demandé). Le type LocalHeroBlock est défini en local dans le block (types.ts non édité)."
      }
    ],
    "fusion": "All clean. Done.\n\nÉdité 4 fichiers partagés pour câbler les 4 héros (`product-hero`, `industrie-hero`, `secteur-hero`, `local-hero`), tous en catégorie « En-têtes ».\n\n**1. `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/index.ts`**\n- 4 imports : `PRODUCT_HERO_FRAGMENT`, `INDUSTRIE_HERO_FRAGMENT`, `SECTEUR_HERO_FRAGMENT`, `LOCAL_HERO_FRAGMENT`.\n- 4 entrées ajoutées au tableau `SECTIONS_FRAGMENTS`.\n- 4 spreads dans `SECTIONS_FIELD` : `...ProductHeroFields`, `...IndustrieHeroFields`, `...SecteurHeroFields`, `...LocalHeroFields` (les fragments réels s'appellent `<Pascal>Fields`, pas le `fragmentSpread` littéral du snippet `product-hero` qui disait `${PRODUCT_HERO_FRAGMENT}` — corrigé en spread GraphQL).\n\n**2. `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/types.ts`**\n- Ajout des interfaces `IndustrieHeroBlock` et `SecteurHeroBlock` (leurs blocks les **importent** depuis `@/lib/wp/types` — obligatoire sinon build cassé).\n- Ajout de ces deux types à l'union `WpSection`.\n- `ProductHeroBlock` et `LocalHeroBlock` **non ajoutés** : ils sont définis inline dans leurs blocks respectifs et ne sont pas exportés/importés depuis types.ts (la consigne dit « si pas déjà inline »). Les ré-écrire ici aurait créé une duplication inutile.\n\n**3. `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/blocks/BlockRenderer.tsx`**\n- 4 imports default : `ProductHero`, `IndustrieHero`, `SecteurHero`, `LocalHero`.\n- 4 clés registry : `ProductHeroLayout`, `IndustrieHeroLayout`, `SecteurHeroLayout`, `LocalHeroLayout` (suffixe Pascal, conforme).\n\n**4. `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/sections.php`**\n- 4 slugs dans `$files`.\n- 4 entrées `'<slug>' => 'En-têtes'` dans `cvb_layout_categories()`.\n- 4 slugs dans `$with_thumb`.\n\n**Vérifications de cohérence faites**\n- registryKey = suffixe `<Pascal>Layout` partout (jamais le snake). Snippets fournissaient déjà les bons registryKey ; aucun snake à corriger.\n- Defaults exports réels : `ProductHero.tsx`→`ProductHero`, `SecteurHero.tsx`→`SecteurHero`, mais `IndustrieHero.tsx`→`IndustrieHeroBlockComponent` et `LocalHero.tsx`→`LocalHeroBlockComponent`. Sans incidence : import default, donc l'alias local Pascal est correct.\n- Types GraphQL : layouts PHP utilisent `cvb_layout('product_hero'/...)` → types `...ProductHeroLayout` etc., cohérents avec fragments et registry.\n- `npx tsc --noEmit` : exit 0 (aucune erreur).\n- `php -l sections.php` : aucune erreur de syntaxe.\n\n**Conflits / notes**\n- Le commentaire de `sections.php` indique « 26 layouts » / « 25/26 vignettes » : déjà obsolète (bien plus de layouts présents). Je ne l'ai pas réécrit (hors périmètre, purement cosmétique). Les 4 héros sont marqués `$with_thumb` comme demandé, mais aucune vignette `.jpg` n'existe encore pour eux dans `block-thumbnails/` → ACFE pointera vers une URL absente tant que la capture n'est pas faite (cf. tâche #6 vignettes).\n- Aucun autre fichier partagé touché. Pas de build lancé (seulement tsc/php -l)."
  }
}