{"parentUuid":null,"isSidechain":true,"promptId":"7f3e3cb7-906e-4e3a-92fd-290df6bb8908","agentId":"a80d1ba9a1945c3c3","type":"user","message":{"role":"user","content":"In the codebase at /opt/projects/.covalba-wp-worktrees/tom-passage-wp (Next.js 16 + headless WordPress with ACF flexible content blocks), there is a concept of \"Composant d'origine\" / \"origine registry\". In the WP backoffice, a block has a field \"Composant d'origine\" described as \"Géré par les développeurs — composant du site qui rend ce block\", with a value like `ProductFAQCovaThermLight`.\n\nI need to fully understand the LOGIC behind this feature to explain it to the user. Investigate and report concretely:\n\n1. Search the whole repo for \"origine\", \"Origine\", \"composant_react\", \"ComposantReact\", \"ProductFAQCovaThermLight\", and any \"registry\" file (e.g. src/components/origine/*, an originRegistry, a map from string name → React component). Report the registry file path and its full content/structure.\n\n2. How does a block in WordPress carry this \"origine\" value? Look in wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/ for the layout that defines a \"composant d'origine\" / \"origine\" / \"composant_react\" field. Report the ACF field definition and which layouts have it.\n\n3. How does the front render it? Find where BlockRenderer (src/components/blocks/BlockRenderer.tsx) or similar maps an incoming block's origine value to an actual React component from the registry. Show the resolution logic (string → component lookup, fallback behavior if not found).\n\n4. Find the actual `ProductFAQCovaThermLight` component (and a couple siblings) — where it lives, what it renders. Explain what \"original component\" means here (these seem to be the original Vite-era components, parametrized and fed by WP).\n\n5. Explain the WHY: based on git history (commits \"feat(parite): original components parametrized + origine registry\", \"pixel-perfect parity with the original Vite version, WP-fed\") and any SCHEMA.md / docs, what problem does this registry solve? How does it relate to the \"1 component = same code, no reimplementation\" parity requirement?\n\nReport exact file paths and concise excerpts. The goal is a clear explanation of the data flow: WP block (origine string) → registry lookup → original React component rendered with WP-provided props. Do NOT propose changes."},"uuid":"a326cc0a-979a-41ee-bc4e-75afe7a0164f","timestamp":"2026-06-14T16:48:01.226Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"a326cc0a-979a-41ee-bc4e-75afe7a0164f","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","attachment":{"type":"deferred_tools_delta","addedNames":["CronCreate","CronDelete","CronList","DesignSync","EnterWorktree","ExitWorktree","ListMcpResourcesTool","Monitor","PushNotification","ReadMcpResourceTool","RemoteTrigger","TaskCreate","TaskGet","TaskList","TaskStop","TaskUpdate","WebFetch","WebSearch","mcp__EstimanouContent__add_article_block","mcp__EstimanouContent__add_block","mcp__EstimanouContent__create_article","mcp__EstimanouContent__create_page","mcp__EstimanouContent__delete_article","mcp__EstimanouContent__delete_article_block","mcp__EstimanouContent__delete_block","mcp__EstimanouContent__get_article","mcp__EstimanouContent__get_city_page","mcp__EstimanouContent__get_page","mcp__EstimanouContent__list_articles","mcp__EstimanouContent__list_available_blocks","mcp__EstimanouContent__list_city_pages","mcp__EstimanouContent__list_pages","mcp__EstimanouContent__list_site_urls","mcp__EstimanouContent__reorder_article_blocks","mcp__EstimanouContent__reorder_blocks","mcp__EstimanouContent__replace_article_content","mcp__EstimanouContent__replace_city_seo_content","mcp__EstimanouContent__replace_page_layout","mcp__EstimanouContent__search_internal_links","mcp__EstimanouContent__update_article","mcp__EstimanouContent__update_article_block","mcp__EstimanouContent__update_block","mcp__EstimanouContent__update_city_page_content","mcp__claude_ai_Ahref__batch-analysis","mcp__claude_ai_Ahref__brand-radar-ai-responses","mcp__claude_ai_Ahref__brand-radar-ai-responses-entities","mcp__claude_ai_Ahref__brand-radar-cited-domains","mcp__claude_ai_Ahref__brand-radar-cited-domains-entities","mcp__claude_ai_Ahref__brand-radar-cited-pages","mcp__claude_ai_Ahref__brand-radar-cited-pages-entities","mcp__claude_ai_Ahref__brand-radar-impressions-history","mcp__claude_ai_Ahref__brand-radar-impressions-overview","mcp__claude_ai_Ahref__brand-radar-impressions-overview-entities","mcp__claude_ai_Ahref__brand-radar-mentions-history","mcp__claude_ai_Ahref__brand-radar-mentions-history-entities","mcp__claude_ai_Ahref__brand-radar-mentions-overview","mcp__claude_ai_Ahref__brand-radar-mentions-overview-entities","mcp__claude_ai_Ahref__brand-radar-sov-history","mcp__claude_ai_Ahref__brand-radar-sov-history-entities","mcp__claude_ai_Ahref__brand-radar-sov-overview","mcp__claude_ai_Ahref__brand-radar-sov-overview-entities","mcp__claude_ai_Ahref__doc","mcp__claude_ai_Ahref__gsc-anonymous-queries","mcp__claude_ai_Ahref__gsc-ctr-by-position","mcp__claude_ai_Ahref__gsc-keyword-history","mcp__claude_ai_Ahref__gsc-keywords","mcp__claude_ai_Ahref__gsc-metrics-by-country","mcp__claude_ai_Ahref__gsc-page-history","mcp__claude_ai_Ahref__gsc-pages","mcp__claude_ai_Ahref__gsc-pages-history","mcp__claude_ai_Ahref__gsc-performance-by-device","mcp__claude_ai_Ahref__gsc-performance-by-position","mcp__claude_ai_Ahref__gsc-performance-history","mcp__claude_ai_Ahref__gsc-positions-history","mcp__claude_ai_Ahref__keywords-explorer-matching-terms","mcp__claude_ai_Ahref__keywords-explorer-overview","mcp__claude_ai_Ahref__keywords-explorer-related-terms","mcp__claude_ai_Ahref__keywords-explorer-search-suggestions","mcp__claude_ai_Ahref__keywords-explorer-volume-by-country","mcp__claude_ai_Ahref__keywords-explorer-volume-history","mcp__claude_ai_Ahref__management-brand-radar-prompts","mcp__claude_ai_Ahref__management-brand-radar-reports","mcp__claude_ai_Ahref__management-keyword-list-keywords","mcp__claude_ai_Ahref__management-locations","mcp__claude_ai_Ahref__management-project-competitors","mcp__claude_ai_Ahref__management-project-keywords","mcp__claude_ai_Ahref__management-projects","mcp__claude_ai_Ahref__public-crawler-ip-ranges","mcp__claude_ai_Ahref__public-crawler-ips","mcp__claude_ai_Ahref__public-domain-rating-free","mcp__claude_ai_Ahref__rank-tracker-competitors-domains","mcp__claude_ai_Ahref__rank-tracker-competitors-overview","mcp__claude_ai_Ahref__rank-tracker-competitors-pages","mcp__claude_ai_Ahref__rank-tracker-competitors-stats","mcp__claude_ai_Ahref__rank-tracker-overview","mcp__claude_ai_Ahref__rank-tracker-serp-overview","mcp__claude_ai_Ahref__render-data-table","mcp__claude_ai_Ahref__render-scorecard","mcp__claude_ai_Ahref__render-time-series-chart","mcp__claude_ai_Ahref__serp-overview","mcp__claude_ai_Ahref__site-audit-issues","mcp__claude_ai_Ahref__site-audit-page-content","mcp__claude_ai_Ahref__site-audit-page-explorer","mcp__claude_ai_Ahref__site-audit-projects","mcp__claude_ai_Ahref__site-explorer-all-backlinks","mcp__claude_ai_Ahref__site-explorer-anchors","mcp__claude_ai_Ahref__site-explorer-backlinks-stats","mcp__claude_ai_Ahref__site-explorer-broken-backlinks","mcp__claude_ai_Ahref__site-explorer-crawled-pages","mcp__claude_ai_Ahref__site-explorer-domain-rating","mcp__claude_ai_Ahref__site-explorer-domain-rating-history","mcp__claude_ai_Ahref__site-explorer-keywords-history","mcp__claude_ai_Ahref__site-explorer-linked-anchors-external","mcp__claude_ai_Ahref__site-explorer-linked-anchors-internal","mcp__claude_ai_Ahref__site-explorer-linked-domains","mcp__claude_ai_Ahref__site-explorer-metrics","mcp__claude_ai_Ahref__site-explorer-metrics-by-country","mcp__claude_ai_Ahref__site-explorer-metrics-history","mcp__claude_ai_Ahref__site-explorer-organic-competitors","mcp__claude_ai_Ahref__site-explorer-organic-keywords","mcp__claude_ai_Ahref__site-explorer-outlinks-stats","mcp__claude_ai_Ahref__site-explorer-pages-by-backlinks","mcp__claude_ai_Ahref__site-explorer-pages-by-internal-links","mcp__claude_ai_Ahref__site-explorer-pages-by-traffic","mcp__claude_ai_Ahref__site-explorer-pages-history","mcp__claude_ai_Ahref__site-explorer-paid-pages","mcp__claude_ai_Ahref__site-explorer-refdomains-history","mcp__claude_ai_Ahref__site-explorer-referring-domains","mcp__claude_ai_Ahref__site-explorer-top-pages","mcp__claude_ai_Ahref__site-explorer-total-search-volume-history","mcp__claude_ai_Ahref__site-explorer-url-rating-history","mcp__claude_ai_Ahref__social-media-activity-history","mcp__claude_ai_Ahref__social-media-authors","mcp__claude_ai_Ahref__social-media-channel-metrics","mcp__claude_ai_Ahref__social-media-channels","mcp__claude_ai_Ahref__social-media-post-metrics","mcp__claude_ai_Ahref__social-media-posts","mcp__claude_ai_Ahref__subscription-info-limits-and-usage","mcp__claude_ai_Ahref__web-analytics-browser-versions","mcp__claude_ai_Ahref__web-analytics-browser-versions-chart","mcp__claude_ai_Ahref__web-analytics-browsers","mcp__claude_ai_Ahref__web-analytics-browsers-chart","mcp__claude_ai_Ahref__web-analytics-chart","mcp__claude_ai_Ahref__web-analytics-cities","mcp__claude_ai_Ahref__web-analytics-cities-chart","mcp__claude_ai_Ahref__web-analytics-continents","mcp__claude_ai_Ahref__web-analytics-continents-chart","mcp__claude_ai_Ahref__web-analytics-countries","mcp__claude_ai_Ahref__web-analytics-countries-chart","mcp__claude_ai_Ahref__web-analytics-devices","mcp__claude_ai_Ahref__web-analytics-devices-chart","mcp__claude_ai_Ahref__web-analytics-entry-pages","mcp__claude_ai_Ahref__web-analytics-entry-pages-chart","mcp__claude_ai_Ahref__web-analytics-exit-pages","mcp__claude_ai_Ahref__web-analytics-exit-pages-chart","mcp__claude_ai_Ahref__web-analytics-languages","mcp__claude_ai_Ahref__web-analytics-languages-chart","mcp__claude_ai_Ahref__web-analytics-operating-systems","mcp__claude_ai_Ahref__web-analytics-operating-systems-chart","mcp__claude_ai_Ahref__web-analytics-operating-systems-versions","mcp__claude_ai_Ahref__web-analytics-operating-systems-versions-chart","mcp__claude_ai_Ahref__web-analytics-referrers","mcp__claude_ai_Ahref__web-analytics-referrers-chart","mcp__claude_ai_Ahref__web-analytics-source-channels","mcp__claude_ai_Ahref__web-analytics-source-channels-chart","mcp__claude_ai_Ahref__web-analytics-sources","mcp__claude_ai_Ahref__web-analytics-sources-chart","mcp__claude_ai_Ahref__web-analytics-stats","mcp__claude_ai_Ahref__web-analytics-top-pages","mcp__claude_ai_Ahref__web-analytics-top-pages-chart","mcp__claude_ai_Ahref__web-analytics-utm-params","mcp__claude_ai_Ahref__web-analytics-utm-params-chart","mcp__claude_ai_Canva__cancel-editing-transaction","mcp__claude_ai_Canva__comment-on-design","mcp__claude_ai_Canva__commit-editing-transaction","mcp__claude_ai_Canva__copy-design","mcp__claude_ai_Canva__create-brand-template-draft","mcp__claude_ai_Canva__create-design-from-brand-template","mcp__claude_ai_Canva__create-design-from-candidate","mcp__claude_ai_Canva__create-folder","mcp__claude_ai_Canva__export-design","mcp__claude_ai_Canva__generate-design","mcp__claude_ai_Canva__generate-design-structured","mcp__claude_ai_Canva__get-assets","mcp__claude_ai_Canva__get-brand-template-dataset","mcp__claude_ai_Canva__get-design","mcp__claude_ai_Canva__get-design-candidates","mcp__claude_ai_Canva__get-design-content","mcp__claude_ai_Canva__get-design-pages","mcp__claude_ai_Canva__get-design-thumbnail","mcp__claude_ai_Canva__get-export-formats","mcp__claude_ai_Canva__get-presenter-notes","mcp__claude_ai_Canva__help","mcp__claude_ai_Canva__import-design-from-url","mcp__claude_ai_Canva__list-brand-kits","mcp__claude_ai_Canva__list-comments","mcp__claude_ai_Canva__list-folder-items","mcp__claude_ai_Canva__list-replies","mcp__claude_ai_Canva__merge-designs","mcp__claude_ai_Canva__move-item-to-folder","mcp__claude_ai_Canva__perform-editing-operations","mcp__claude_ai_Canva__publish-brand-template","mcp__claude_ai_Canva__reply-to-comment","mcp__claude_ai_Canva__request-outline-review","mcp__claude_ai_Canva__resize-design","mcp__claude_ai_Canva__resolve-shortlink","mcp__claude_ai_Canva__search-brand-templates","mcp__claude_ai_Canva__search-designs","mcp__claude_ai_Canva__search-folders","mcp__claude_ai_Canva__start-editing-transaction","mcp__claude_ai_Canva__upload-asset-from-url","mcp__claude_ai_DATA_GOUV_FR__get_dataservice_info","mcp__claude_ai_DATA_GOUV_FR__get_dataservice_openapi_spec","mcp__claude_ai_DATA_GOUV_FR__get_dataset_info","mcp__claude_ai_DATA_GOUV_FR__get_metrics","mcp__claude_ai_DATA_GOUV_FR__get_resource_info","mcp__claude_ai_DATA_GOUV_FR__list_dataset_resources","mcp__claude_ai_DATA_GOUV_FR__query_resource_data","mcp__claude_ai_DATA_GOUV_FR__search_dataservices","mcp__claude_ai_DATA_GOUV_FR__search_datasets","mcp__claude_ai_DATA_GOUV_FR__search_organizations","mcp__claude_ai_Data_For_SEO__authenticate","mcp__claude_ai_Data_For_SEO__complete_authentication","mcp__claude_ai_Esteworld-Body__add_block","mcp__claude_ai_Esteworld-Body__delete_block","mcp__claude_ai_Esteworld-Body__find_esteworld_page","mcp__claude_ai_Esteworld-Body__fix_global_blocks","mcp__claude_ai_Esteworld-Body__get_block","mcp__claude_ai_Esteworld-Body__get_blocks_library","mcp__claude_ai_Esteworld-Body__get_breadcrumb","mcp__claude_ai_Esteworld-Body__get_fr_en_mapping","mcp__claude_ai_Esteworld-Body__get_internal_links","mcp__claude_ai_Esteworld-Body__get_internal_links_en","mcp__claude_ai_Esteworld-Body__get_maillage_rules","mcp__claude_ai_Esteworld-Body__get_page","mcp__claude_ai_Esteworld-Body__get_page_json","mcp__claude_ai_Esteworld-Body__get_page_status","mcp__claude_ai_Esteworld-Body__get_redirections","mcp__claude_ai_Esteworld-Body__get_section_content","mcp__claude_ai_Esteworld-Body__get_thematique_pages","mcp__claude_ai_Esteworld-Body__get_translation_languages","mcp__claude_ai_Esteworld-Body__list_pages","mcp__claude_ai_Esteworld-Body__list_pages_by_thematique","mcp__claude_ai_Esteworld-Body__list_pages_by_type","mcp__claude_ai_Esteworld-Body__list_pages_to_create","mcp__claude_ai_Esteworld-Body__list_pages_to_rewrite","mcp__claude_ai_Esteworld-Body__list_pages_with_esteworld","mcp__claude_ai_Esteworld-Body__list_pages_without_esteworld","mcp__claude_ai_Esteworld-Body__n8n_activate_workflow","mcp__claude_ai_Esteworld-Body__n8n_get_executions","mcp__claude_ai_Esteworld-Body__n8n_get_workflow","mcp__claude_ai_Esteworld-Body__n8n_list_workflows","mcp__claude_ai_Esteworld-Body__n8n_trigger_webhook","mcp__claude_ai_Esteworld-Body__preview_global_fixes","mcp__claude_ai_Esteworld-Body__reorder_blocks","mcp__claude_ai_Esteworld-Body__search_content","mcp__claude_ai_Esteworld-Body__search_links_by_keyword","mcp__claude_ai_Esteworld-Body__search_links_by_keyword_en","mcp__claude_ai_Esteworld-Body__submit_page","mcp__claude_ai_Esteworld-Body__submit_page_light","mcp__claude_ai_Esteworld-Body__translate_page","mcp__claude_ai_Esteworld-Body__update_block","mcp__claude_ai_Figma__add_code_connect_map","mcp__claude_ai_Figma__create_new_file","mcp__claude_ai_Figma__download_assets","mcp__claude_ai_Figma__generate_diagram","mcp__claude_ai_Figma__get_code_connect_map","mcp__claude_ai_Figma__get_code_connect_suggestions","mcp__claude_ai_Figma__get_context_for_code_connect","mcp__claude_ai_Figma__get_design_context","mcp__claude_ai_Figma__get_figjam","mcp__claude_ai_Figma__get_libraries","mcp__claude_ai_Figma__get_metadata","mcp__claude_ai_Figma__get_screenshot","mcp__claude_ai_Figma__get_variable_defs","mcp__claude_ai_Figma__search_design_system","mcp__claude_ai_Figma__send_code_connect_mappings","mcp__claude_ai_Figma__upload_assets","mcp__claude_ai_Figma__use_figma","mcp__claude_ai_Figma__whoami","mcp__claude_ai_Gmail__create_draft","mcp__claude_ai_Gmail__create_label","mcp__claude_ai_Gmail__delete_label","mcp__claude_ai_Gmail__get_thread","mcp__claude_ai_Gmail__label_message","mcp__claude_ai_Gmail__label_thread","mcp__claude_ai_Gmail__list_drafts","mcp__claude_ai_Gmail__list_labels","mcp__claude_ai_Gmail__search_threads","mcp__claude_ai_Gmail__unlabel_message","mcp__claude_ai_Gmail__unlabel_thread","mcp__claude_ai_Gmail__update_label","mcp__claude_ai_Google_Calendar__create_event","mcp__claude_ai_Google_Calendar__delete_event","mcp__claude_ai_Google_Calendar__get_event","mcp__claude_ai_Google_Calendar__list_calendars","mcp__claude_ai_Google_Calendar__list_events","mcp__claude_ai_Google_Calendar__respond_to_event","mcp__claude_ai_Google_Calendar__suggest_time","mcp__claude_ai_Google_Calendar__update_event","mcp__claude_ai_Google_Drive__copy_file","mcp__claude_ai_Google_Drive__create_file","mcp__claude_ai_Google_Drive__download_file_content","mcp__claude_ai_Google_Drive__get_file_metadata","mcp__claude_ai_Google_Drive__get_file_permissions","mcp__claude_ai_Google_Drive__list_recent_files","mcp__claude_ai_Google_Drive__read_file_content","mcp__claude_ai_Google_Drive__search_files","mcp__claude_ai_SEMRush__backlink_research","mcp__claude_ai_SEMRush__execute_report","mcp__claude_ai_SEMRush__get_report_schema","mcp__claude_ai_SEMRush__keyword_research","mcp__claude_ai_SEMRush__organic_research","mcp__claude_ai_SEMRush__overview_research","mcp__claude_ai_SEMRush__projects_research","mcp__claude_ai_SEMRush__siteaudit_research","mcp__claude_ai_SEMRush__subdomain_research","mcp__claude_ai_SEMRush__subfolder_research","mcp__claude_ai_SEMRush__tracking_research","mcp__claude_ai_SEMRush__trends_research","mcp__claude_ai_SEMRush__url_research","mcp__claude_ai_Vercel__add_toolbar_reaction","mcp__claude_ai_Vercel__change_toolbar_thread_resolve_status","mcp__claude_ai_Vercel__check_domain_availability_and_price","mcp__claude_ai_Vercel__deploy_to_vercel","mcp__claude_ai_Vercel__edit_toolbar_message","mcp__claude_ai_Vercel__get_access_to_vercel_url","mcp__claude_ai_Vercel__get_deployment","mcp__claude_ai_Vercel__get_deployment_build_logs","mcp__claude_ai_Vercel__get_project","mcp__claude_ai_Vercel__get_runtime_logs","mcp__claude_ai_Vercel__get_toolbar_thread","mcp__claude_ai_Vercel__list_deployments","mcp__claude_ai_Vercel__list_projects","mcp__claude_ai_Vercel__list_teams","mcp__claude_ai_Vercel__list_toolbar_threads","mcp__claude_ai_Vercel__reply_to_toolbar_thread","mcp__claude_ai_Vercel__search_vercel_documentation","mcp__claude_ai_Vercel__web_fetch_vercel_url","mcp__gpt-image-2__continue_edit_session","mcp__gpt-image-2__edit_image","mcp__gpt-image-2__end_edit_session","mcp__gpt-image-2__generate_image","mcp__gpt-image-2__list_edit_sessions","mcp__gpt-image-2__start_edit_session","mcp__hostinger__DNS_deleteDNSRecordsV1","mcp__hostinger__DNS_getDNSRecordsV1","mcp__hostinger__DNS_getDNSSnapshotListV1","mcp__hostinger__DNS_getDNSSnapshotV1","mcp__hostinger__DNS_resetDNSRecordsV1","mcp__hostinger__DNS_restoreDNSSnapshotV1","mcp__hostinger__DNS_updateDNSRecordsV1","mcp__hostinger__DNS_validateDNSRecordsV1","mcp__hostinger__VPS_activateFirewallV1","mcp__hostinger__VPS_attachPublicKeyV1","mcp__hostinger__VPS_createFirewallRuleV1","mcp__hostinger__VPS_createNewFirewallV1","mcp__hostinger__VPS_createNewProjectV1","mcp__hostinger__VPS_createPTRRecordV1","mcp__hostinger__VPS_createPostInstallScriptV1","mcp__hostinger__VPS_createPublicKeyV1","mcp__hostinger__VPS_createSnapshotV1","mcp__hostinger__VPS_deactivateFirewallV1","mcp__hostinger__VPS_deleteFirewallRuleV1","mcp__hostinger__VPS_deleteFirewallV1","mcp__hostinger__VPS_deletePTRRecordV1","mcp__hostinger__VPS_deletePostInstallScriptV1","mcp__hostinger__VPS_deleteProjectV1","mcp__hostinger__VPS_deletePublicKeyV1","mcp__hostinger__VPS_deleteSnapshotV1","mcp__hostinger__VPS_getActionDetailsV1","mcp__hostinger__VPS_getActionsV1","mcp__hostinger__VPS_getAttachedPublicKeysV1","mcp__hostinger__VPS_getBackupsV1","mcp__hostinger__VPS_getDataCenterListV1","mcp__hostinger__VPS_getFirewallDetailsV1","mcp__hostinger__VPS_getFirewallListV1","mcp__hostinger__VPS_getMetricsV1","mcp__hostinger__VPS_getPostInstallScriptV1","mcp__hostinger__VPS_getPostInstallScriptsV1","mcp__hostinger__VPS_getProjectContainersV1","mcp__hostinger__VPS_getProjectContentsV1","mcp__hostinger__VPS_getProjectListV1","mcp__hostinger__VPS_getProjectLogsV1","mcp__hostinger__VPS_getPublicKeysV1","mcp__hostinger__VPS_getScanMetricsV1","mcp__hostinger__VPS_getSnapshotV1","mcp__hostinger__VPS_getTemplateDetailsV1","mcp__hostinger__VPS_getTemplatesV1","mcp__hostinger__VPS_getVirtualMachineDetailsV1","mcp__hostinger__VPS_getVirtualMachinesV1","mcp__hostinger__VPS_installMonarxV1","mcp__hostinger__VPS_purchaseNewVirtualMachineV1","mcp__hostinger__VPS_recreateVirtualMachineV1","mcp__hostinger__VPS_resetHostnameV1","mcp__hostinger__VPS_restartProjectV1","mcp__hostinger__VPS_restartVirtualMachineV1","mcp__hostinger__VPS_restoreBackupV1","mcp__hostinger__VPS_restoreSnapshotV1","mcp__hostinger__VPS_setHostnameV1","mcp__hostinger__VPS_setNameserversV1","mcp__hostinger__VPS_setPanelPasswordV1","mcp__hostinger__VPS_setRootPasswordV1","mcp__hostinger__VPS_setupPurchasedVirtualMachineV1","mcp__hostinger__VPS_startProjectV1","mcp__hostinger__VPS_startRecoveryModeV1","mcp__hostinger__VPS_startVirtualMachineV1","mcp__hostinger__VPS_stopProjectV1","mcp__hostinger__VPS_stopRecoveryModeV1","mcp__hostinger__VPS_stopVirtualMachineV1","mcp__hostinger__VPS_syncFirewallV1","mcp__hostinger__VPS_uninstallMonarxV1","mcp__hostinger__VPS_updateFirewallRuleV1","mcp__hostinger__VPS_updatePostInstallScriptV1","mcp__hostinger__VPS_updateProjectV1","mcp__hostinger__billing_deletePaymentMethodV1","mcp__hostinger__billing_disableAutoRenewalV1","mcp__hostinger__billing_enableAutoRenewalV1","mcp__hostinger__billing_getCatalogItemListV1","mcp__hostinger__billing_getPaymentMethodListV1","mcp__hostinger__billing_getSubscriptionListV1","mcp__hostinger__billing_setDefaultPaymentMethodV1","mcp__hostinger__domains_checkDomainAvailabilityV1","mcp__hostinger__domains_createDomainForwardingV1","mcp__hostinger__domains_createWHOISProfileV1","mcp__hostinger__domains_deleteDomainForwardingV1","mcp__hostinger__domains_deleteWHOISProfileV1","mcp__hostinger__domains_disableDomainLockV1","mcp__hostinger__domains_disablePrivacyProtectionV1","mcp__hostinger__domains_enableDomainLockV1","mcp__hostinger__domains_enablePrivacyProtectionV1","mcp__hostinger__domains_getDomainDetailsV1","mcp__hostinger__domains_getDomainForwardingV1","mcp__hostinger__domains_getDomainListV1","mcp__hostinger__domains_getWHOISProfileListV1","mcp__hostinger__domains_getWHOISProfileUsageV1","mcp__hostinger__domains_getWHOISProfileV1","mcp__hostinger__domains_purchaseNewDomainV1","mcp__hostinger__domains_updateDomainNameserversV1","mcp__hostinger__hosting_createAccountDatabaseV1","mcp__hostinger__hosting_createWebsiteParkedDomainV1","mcp__hostinger__hosting_createWebsiteSubdomainV1","mcp__hostinger__hosting_createWebsiteV1","mcp__hostinger__hosting_deleteAccountDatabaseV1","mcp__hostinger__hosting_deleteWebsiteParkedDomainV1","mcp__hostinger__hosting_deleteWebsiteSubdomainV1","mcp__hostinger__hosting_deployJsApplication","mcp__hostinger__hosting_deployStaticWebsite","mcp__hostinger__hosting_deployWordpressPlugin","mcp__hostinger__hosting_deployWordpressTheme","mcp__hostinger__hosting_generateAFreeSubdomainV1","mcp__hostinger__hosting_importWordpressWebsite","mcp__hostinger__hosting_installWordPressV1","mcp__hostinger__hosting_listAccountDatabasesV1","mcp__hostinger__hosting_listAvailableDatacentersV1","mcp__hostinger__hosting_listJsDeployments","mcp__hostinger__hosting_listOrdersV1","mcp__hostinger__hosting_listWebsiteParkedDomainsV1","mcp__hostinger__hosting_listWebsiteSubdomainsV1","mcp__hostinger__hosting_listWebsitesV1","mcp__hostinger__hosting_listWordPressInstallationsV1","mcp__hostinger__hosting_showJsDeploymentLogs","mcp__hostinger__hosting_verifyDomainOwnershipV1","mcp__hostinger__reach_createANewContactSegmentV1","mcp__hostinger__reach_createANewContactV1","mcp__hostinger__reach_createNewContactsV1","mcp__hostinger__reach_deleteAContactV1","mcp__hostinger__reach_getSegmentDetailsV1","mcp__hostinger__reach_listContactGroupsV1","mcp__hostinger__reach_listContactsV1","mcp__hostinger__reach_listProfilesV1","mcp__hostinger__reach_listSegmentContactsV1","mcp__hostinger__reach_listSegmentsV1","mcp__hostinger__v2_getDomainVerificationsDIRECT","mcp__n8n-mcp__get_node","mcp__n8n-mcp__get_template","mcp__n8n-mcp__n8n_audit_instance","mcp__n8n-mcp__n8n_autofix_workflow","mcp__n8n-mcp__n8n_create_workflow","mcp__n8n-mcp__n8n_delete_workflow","mcp__n8n-mcp__n8n_deploy_template","mcp__n8n-mcp__n8n_executions","mcp__n8n-mcp__n8n_generate_workflow","mcp__n8n-mcp__n8n_get_workflow","mcp__n8n-mcp__n8n_health_check","mcp__n8n-mcp__n8n_list_workflows","mcp__n8n-mcp__n8n_manage_credentials","mcp__n8n-mcp__n8n_manage_datatable","mcp__n8n-mcp__n8n_test_workflow","mcp__n8n-mcp__n8n_update_full_workflow","mcp__n8n-mcp__n8n_update_partial_workflow","mcp__n8n-mcp__n8n_validate_workflow","mcp__n8n-mcp__n8n_workflow_versions","mcp__n8n-mcp__search_nodes","mcp__n8n-mcp__search_templates","mcp__n8n-mcp__tools_documentation","mcp__n8n-mcp__validate_node","mcp__n8n-mcp__validate_workflow","mcp__nanobanana__generate_image","mcp__nanobanana__maintenance","mcp__nanobanana__show_output_stats","mcp__nanobanana__upload_file","mcp__plugin_vercel_vercel__authenticate","mcp__plugin_vercel_vercel__complete_authentication","mcp__refero__refero_get_flow","mcp__refero__refero_get_screen","mcp__refero__refero_get_screen_image","mcp__refero__refero_get_similar_screens","mcp__refero__refero_get_style","mcp__refero__refero_search_flows","mcp__refero__refero_search_screens","mcp__refero__refero_search_styles","mcp__semrush__authenticate","mcp__semrush__complete_authentication","mcp__stock-images-mcp__search_stock_images","mcp__trello__add_card_to_list","mcp__trello__add_checklist_item","mcp__trello__add_comment","mcp__trello__add_list_to_board","mcp__trello__archive_card","mcp__trello__archive_list","mcp__trello__assign_member_to_card","mcp__trello__attach_file_to_card","mcp__trello__attach_image_data_to_card","mcp__trello__attach_image_to_card","mcp__trello__create_board","mcp__trello__create_checklist","mcp__trello__create_label","mcp__trello__delete_comment","mcp__trello__delete_label","mcp__trello__find_checklist_items_by_description","mcp__trello__get_acceptance_criteria","mcp__trello__get_active_board_info","mcp__trello__get_board_labels","mcp__trello__get_board_members","mcp__trello__get_card","mcp__trello__get_card_comments","mcp__trello__get_card_history","mcp__trello__get_cards_by_list_id","mcp__trello__get_checklist_by_name","mcp__trello__get_checklist_items","mcp__trello__get_health","mcp__trello__get_health_detailed","mcp__trello__get_health_metadata","mcp__trello__get_health_performance","mcp__trello__get_lists","mcp__trello__get_my_cards","mcp__trello__get_recent_activity","mcp__trello__list_boards","mcp__trello__list_boards_in_workspace","mcp__trello__list_workspaces","mcp__trello__move_card","mcp__trello__perform_system_repair","mcp__trello__remove_member_from_card","mcp__trello__set_active_board","mcp__trello__set_active_workspace","mcp__trello__update_card_details","mcp__trello__update_checklist_item","mcp__trello__update_comment","mcp__trello__update_label"],"addedLines":["CronCreate","CronDelete","CronList","DesignSync","EnterWorktree","ExitWorktree","ListMcpResourcesTool","Monitor","PushNotification","ReadMcpResourceTool","RemoteTrigger","TaskCreate","TaskGet","TaskList","TaskStop","TaskUpdate","WebFetch","WebSearch","mcp__EstimanouContent__add_article_block","mcp__EstimanouContent__add_block","mcp__EstimanouContent__create_article","mcp__EstimanouContent__create_page","mcp__EstimanouContent__delete_article","mcp__EstimanouContent__delete_article_block","mcp__EstimanouContent__delete_block","mcp__EstimanouContent__get_article","mcp__EstimanouContent__get_city_page","mcp__EstimanouContent__get_page","mcp__EstimanouContent__list_articles","mcp__EstimanouContent__list_available_blocks","mcp__EstimanouContent__list_city_pages","mcp__EstimanouContent__list_pages","mcp__EstimanouContent__list_site_urls","mcp__EstimanouContent__reorder_article_blocks","mcp__EstimanouContent__reorder_blocks","mcp__EstimanouContent__replace_article_content","mcp__EstimanouContent__replace_city_seo_content","mcp__EstimanouContent__replace_page_layout","mcp__EstimanouContent__search_internal_links","mcp__EstimanouContent__update_article","mcp__EstimanouContent__update_article_block","mcp__EstimanouContent__update_block","mcp__EstimanouContent__update_city_page_content","mcp__claude_ai_Ahref__batch-analysis","mcp__claude_ai_Ahref__brand-radar-ai-responses","mcp__claude_ai_Ahref__brand-radar-ai-responses-entities","mcp__claude_ai_Ahref__brand-radar-cited-domains","mcp__claude_ai_Ahref__brand-radar-cited-domains-entities","mcp__claude_ai_Ahref__brand-radar-cited-pages","mcp__claude_ai_Ahref__brand-radar-cited-pages-entities","mcp__claude_ai_Ahref__brand-radar-impressions-history","mcp__claude_ai_Ahref__brand-radar-impressions-overview","mcp__claude_ai_Ahref__brand-radar-impressions-overview-entities","mcp__claude_ai_Ahref__brand-radar-mentions-history","mcp__claude_ai_Ahref__brand-radar-mentions-history-entities","mcp__claude_ai_Ahref__brand-radar-mentions-overview","mcp__claude_ai_Ahref__brand-radar-mentions-overview-entities","mcp__claude_ai_Ahref__brand-radar-sov-history","mcp__claude_ai_Ahref__brand-radar-sov-history-entities","mcp__claude_ai_Ahref__brand-radar-sov-overview","mcp__claude_ai_Ahref__brand-radar-sov-overview-entities","mcp__claude_ai_Ahref__doc","mcp__claude_ai_Ahref__gsc-anonymous-queries","mcp__claude_ai_Ahref__gsc-ctr-by-position","mcp__claude_ai_Ahref__gsc-keyword-history","mcp__claude_ai_Ahref__gsc-keywords","mcp__claude_ai_Ahref__gsc-metrics-by-country","mcp__claude_ai_Ahref__gsc-page-history","mcp__claude_ai_Ahref__gsc-pages","mcp__claude_ai_Ahref__gsc-pages-history","mcp__claude_ai_Ahref__gsc-performance-by-device","mcp__claude_ai_Ahref__gsc-performance-by-position","mcp__claude_ai_Ahref__gsc-performance-history","mcp__claude_ai_Ahref__gsc-positions-history","mcp__claude_ai_Ahref__keywords-explorer-matching-terms","mcp__claude_ai_Ahref__keywords-explorer-overview","mcp__claude_ai_Ahref__keywords-explorer-related-terms","mcp__claude_ai_Ahref__keywords-explorer-search-suggestions","mcp__claude_ai_Ahref__keywords-explorer-volume-by-country","mcp__claude_ai_Ahref__keywords-explorer-volume-history","mcp__claude_ai_Ahref__management-brand-radar-prompts","mcp__claude_ai_Ahref__management-brand-radar-reports","mcp__claude_ai_Ahref__management-keyword-list-keywords","mcp__claude_ai_Ahref__management-locations","mcp__claude_ai_Ahref__management-project-competitors","mcp__claude_ai_Ahref__management-project-keywords","mcp__claude_ai_Ahref__management-projects","mcp__claude_ai_Ahref__public-crawler-ip-ranges","mcp__claude_ai_Ahref__public-crawler-ips","mcp__claude_ai_Ahref__public-domain-rating-free","mcp__claude_ai_Ahref__rank-tracker-competitors-domains","mcp__claude_ai_Ahref__rank-tracker-competitors-overview","mcp__claude_ai_Ahref__rank-tracker-competitors-pages","mcp__claude_ai_Ahref__rank-tracker-competitors-stats","mcp__claude_ai_Ahref__rank-tracker-overview","mcp__claude_ai_Ahref__rank-tracker-serp-overview","mcp__claude_ai_Ahref__render-data-table","mcp__claude_ai_Ahref__render-scorecard","mcp__claude_ai_Ahref__render-time-series-chart","mcp__claude_ai_Ahref__serp-overview","mcp__claude_ai_Ahref__site-audit-issues","mcp__claude_ai_Ahref__site-audit-page-content","mcp__claude_ai_Ahref__site-audit-page-explorer","mcp__claude_ai_Ahref__site-audit-projects","mcp__claude_ai_Ahref__site-explorer-all-backlinks","mcp__claude_ai_Ahref__site-explorer-anchors","mcp__claude_ai_Ahref__site-explorer-backlinks-stats","mcp__claude_ai_Ahref__site-explorer-broken-backlinks","mcp__claude_ai_Ahref__site-explorer-crawled-pages","mcp__claude_ai_Ahref__site-explorer-domain-rating","mcp__claude_ai_Ahref__site-explorer-domain-rating-history","mcp__claude_ai_Ahref__site-explorer-keywords-history","mcp__claude_ai_Ahref__site-explorer-linked-anchors-external","mcp__claude_ai_Ahref__site-explorer-linked-anchors-internal","mcp__claude_ai_Ahref__site-explorer-linked-domains","mcp__claude_ai_Ahref__site-explorer-metrics","mcp__claude_ai_Ahref__site-explorer-metrics-by-country","mcp__claude_ai_Ahref__site-explorer-metrics-history","mcp__claude_ai_Ahref__site-explorer-organic-competitors","mcp__claude_ai_Ahref__site-explorer-organic-keywords","mcp__claude_ai_Ahref__site-explorer-outlinks-stats","mcp__claude_ai_Ahref__site-explorer-pages-by-backlinks","mcp__claude_ai_Ahref__site-explorer-pages-by-internal-links","mcp__claude_ai_Ahref__site-explorer-pages-by-traffic","mcp__claude_ai_Ahref__site-explorer-pages-history","mcp__claude_ai_Ahref__site-explorer-paid-pages","mcp__claude_ai_Ahref__site-explorer-refdomains-history","mcp__claude_ai_Ahref__site-explorer-referring-domains","mcp__claude_ai_Ahref__site-explorer-top-pages","mcp__claude_ai_Ahref__site-explorer-total-search-volume-history","mcp__claude_ai_Ahref__site-explorer-url-rating-history","mcp__claude_ai_Ahref__social-media-activity-history","mcp__claude_ai_Ahref__social-media-authors","mcp__claude_ai_Ahref__social-media-channel-metrics","mcp__claude_ai_Ahref__social-media-channels","mcp__claude_ai_Ahref__social-media-post-metrics","mcp__claude_ai_Ahref__social-media-posts","mcp__claude_ai_Ahref__subscription-info-limits-and-usage","mcp__claude_ai_Ahref__web-analytics-browser-versions","mcp__claude_ai_Ahref__web-analytics-browser-versions-chart","mcp__claude_ai_Ahref__web-analytics-browsers","mcp__claude_ai_Ahref__web-analytics-browsers-chart","mcp__claude_ai_Ahref__web-analytics-chart","mcp__claude_ai_Ahref__web-analytics-cities","mcp__claude_ai_Ahref__web-analytics-cities-chart","mcp__claude_ai_Ahref__web-analytics-continents","mcp__claude_ai_Ahref__web-analytics-continents-chart","mcp__claude_ai_Ahref__web-analytics-countries","mcp__claude_ai_Ahref__web-analytics-countries-chart","mcp__claude_ai_Ahref__web-analytics-devices","mcp__claude_ai_Ahref__web-analytics-devices-chart","mcp__claude_ai_Ahref__web-analytics-entry-pages","mcp__claude_ai_Ahref__web-analytics-entry-pages-chart","mcp__claude_ai_Ahref__web-analytics-exit-pages","mcp__claude_ai_Ahref__web-analytics-exit-pages-chart","mcp__claude_ai_Ahref__web-analytics-languages","mcp__claude_ai_Ahref__web-analytics-languages-chart","mcp__claude_ai_Ahref__web-analytics-operating-systems","mcp__claude_ai_Ahref__web-analytics-operating-systems-chart","mcp__claude_ai_Ahref__web-analytics-operating-systems-versions","mcp__claude_ai_Ahref__web-analytics-operating-systems-versions-chart","mcp__claude_ai_Ahref__web-analytics-referrers","mcp__claude_ai_Ahref__web-analytics-referrers-chart","mcp__claude_ai_Ahref__web-analytics-source-channels","mcp__claude_ai_Ahref__web-analytics-source-channels-chart","mcp__claude_ai_Ahref__web-analytics-sources","mcp__claude_ai_Ahref__web-analytics-sources-chart","mcp__claude_ai_Ahref__web-analytics-stats","mcp__claude_ai_Ahref__web-analytics-top-pages","mcp__claude_ai_Ahref__web-analytics-top-pages-chart","mcp__claude_ai_Ahref__web-analytics-utm-params","mcp__claude_ai_Ahref__web-analytics-utm-params-chart","mcp__claude_ai_Canva__cancel-editing-transaction","mcp__claude_ai_Canva__comment-on-design","mcp__claude_ai_Canva__commit-editing-transaction","mcp__claude_ai_Canva__copy-design","mcp__claude_ai_Canva__create-brand-template-draft","mcp__claude_ai_Canva__create-design-from-brand-template","mcp__claude_ai_Canva__create-design-from-candidate","mcp__claude_ai_Canva__create-folder","mcp__claude_ai_Canva__export-design","mcp__claude_ai_Canva__generate-design","mcp__claude_ai_Canva__generate-design-structured","mcp__claude_ai_Canva__get-assets","mcp__claude_ai_Canva__get-brand-template-dataset","mcp__claude_ai_Canva__get-design","mcp__claude_ai_Canva__get-design-candidates","mcp__claude_ai_Canva__get-design-content","mcp__claude_ai_Canva__get-design-pages","mcp__claude_ai_Canva__get-design-thumbnail","mcp__claude_ai_Canva__get-export-formats","mcp__claude_ai_Canva__get-presenter-notes","mcp__claude_ai_Canva__help","mcp__claude_ai_Canva__import-design-from-url","mcp__claude_ai_Canva__list-brand-kits","mcp__claude_ai_Canva__list-comments","mcp__claude_ai_Canva__list-folder-items","mcp__claude_ai_Canva__list-replies","mcp__claude_ai_Canva__merge-designs","mcp__claude_ai_Canva__move-item-to-folder","mcp__claude_ai_Canva__perform-editing-operations","mcp__claude_ai_Canva__publish-brand-template","mcp__claude_ai_Canva__reply-to-comment","mcp__claude_ai_Canva__request-outline-review","mcp__claude_ai_Canva__resize-design","mcp__claude_ai_Canva__resolve-shortlink","mcp__claude_ai_Canva__search-brand-templates","mcp__claude_ai_Canva__search-designs","mcp__claude_ai_Canva__search-folders","mcp__claude_ai_Canva__start-editing-transaction","mcp__claude_ai_Canva__upload-asset-from-url","mcp__claude_ai_DATA_GOUV_FR__get_dataservice_info","mcp__claude_ai_DATA_GOUV_FR__get_dataservice_openapi_spec","mcp__claude_ai_DATA_GOUV_FR__get_dataset_info","mcp__claude_ai_DATA_GOUV_FR__get_metrics","mcp__claude_ai_DATA_GOUV_FR__get_resource_info","mcp__claude_ai_DATA_GOUV_FR__list_dataset_resources","mcp__claude_ai_DATA_GOUV_FR__query_resource_data","mcp__claude_ai_DATA_GOUV_FR__search_dataservices","mcp__claude_ai_DATA_GOUV_FR__search_datasets","mcp__claude_ai_DATA_GOUV_FR__search_organizations","mcp__claude_ai_Data_For_SEO__authenticate","mcp__claude_ai_Data_For_SEO__complete_authentication","mcp__claude_ai_Esteworld-Body__add_block","mcp__claude_ai_Esteworld-Body__delete_block","mcp__claude_ai_Esteworld-Body__find_esteworld_page","mcp__claude_ai_Esteworld-Body__fix_global_blocks","mcp__claude_ai_Esteworld-Body__get_block","mcp__claude_ai_Esteworld-Body__get_blocks_library","mcp__claude_ai_Esteworld-Body__get_breadcrumb","mcp__claude_ai_Esteworld-Body__get_fr_en_mapping","mcp__claude_ai_Esteworld-Body__get_internal_links","mcp__claude_ai_Esteworld-Body__get_internal_links_en","mcp__claude_ai_Esteworld-Body__get_maillage_rules","mcp__claude_ai_Esteworld-Body__get_page","mcp__claude_ai_Esteworld-Body__get_page_json","mcp__claude_ai_Esteworld-Body__get_page_status","mcp__claude_ai_Esteworld-Body__get_redirections","mcp__claude_ai_Esteworld-Body__get_section_content","mcp__claude_ai_Esteworld-Body__get_thematique_pages","mcp__claude_ai_Esteworld-Body__get_translation_languages","mcp__claude_ai_Esteworld-Body__list_pages","mcp__claude_ai_Esteworld-Body__list_pages_by_thematique","mcp__claude_ai_Esteworld-Body__list_pages_by_type","mcp__claude_ai_Esteworld-Body__list_pages_to_create","mcp__claude_ai_Esteworld-Body__list_pages_to_rewrite","mcp__claude_ai_Esteworld-Body__list_pages_with_esteworld","mcp__claude_ai_Esteworld-Body__list_pages_without_esteworld","mcp__claude_ai_Esteworld-Body__n8n_activate_workflow","mcp__claude_ai_Esteworld-Body__n8n_get_executions","mcp__claude_ai_Esteworld-Body__n8n_get_workflow","mcp__claude_ai_Esteworld-Body__n8n_list_workflows","mcp__claude_ai_Esteworld-Body__n8n_trigger_webhook","mcp__claude_ai_Esteworld-Body__preview_global_fixes","mcp__claude_ai_Esteworld-Body__reorder_blocks","mcp__claude_ai_Esteworld-Body__search_content","mcp__claude_ai_Esteworld-Body__search_links_by_keyword","mcp__claude_ai_Esteworld-Body__search_links_by_keyword_en","mcp__claude_ai_Esteworld-Body__submit_page","mcp__claude_ai_Esteworld-Body__submit_page_light","mcp__claude_ai_Esteworld-Body__translate_page","mcp__claude_ai_Esteworld-Body__update_block","mcp__claude_ai_Figma__add_code_connect_map","mcp__claude_ai_Figma__create_new_file","mcp__claude_ai_Figma__download_assets","mcp__claude_ai_Figma__generate_diagram","mcp__claude_ai_Figma__get_code_connect_map","mcp__claude_ai_Figma__get_code_connect_suggestions","mcp__claude_ai_Figma__get_context_for_code_connect","mcp__claude_ai_Figma__get_design_context","mcp__claude_ai_Figma__get_figjam","mcp__claude_ai_Figma__get_libraries","mcp__claude_ai_Figma__get_metadata","mcp__claude_ai_Figma__get_screenshot","mcp__claude_ai_Figma__get_variable_defs","mcp__claude_ai_Figma__search_design_system","mcp__claude_ai_Figma__send_code_connect_mappings","mcp__claude_ai_Figma__upload_assets","mcp__claude_ai_Figma__use_figma","mcp__claude_ai_Figma__whoami","mcp__claude_ai_Gmail__create_draft","mcp__claude_ai_Gmail__create_label","mcp__claude_ai_Gmail__delete_label","mcp__claude_ai_Gmail__get_thread","mcp__claude_ai_Gmail__label_message","mcp__claude_ai_Gmail__label_thread","mcp__claude_ai_Gmail__list_drafts","mcp__claude_ai_Gmail__list_labels","mcp__claude_ai_Gmail__search_threads","mcp__claude_ai_Gmail__unlabel_message","mcp__claude_ai_Gmail__unlabel_thread","mcp__claude_ai_Gmail__update_label","mcp__claude_ai_Google_Calendar__create_event","mcp__claude_ai_Google_Calendar__delete_event","mcp__claude_ai_Google_Calendar__get_event","mcp__claude_ai_Google_Calendar__list_calendars","mcp__claude_ai_Google_Calendar__list_events","mcp__claude_ai_Google_Calendar__respond_to_event","mcp__claude_ai_Google_Calendar__suggest_time","mcp__claude_ai_Google_Calendar__update_event","mcp__claude_ai_Google_Drive__copy_file","mcp__claude_ai_Google_Drive__create_file","mcp__claude_ai_Google_Drive__download_file_content","mcp__claude_ai_Google_Drive__get_file_metadata","mcp__claude_ai_Google_Drive__get_file_permissions","mcp__claude_ai_Google_Drive__list_recent_files","mcp__claude_ai_Google_Drive__read_file_content","mcp__claude_ai_Google_Drive__search_files","mcp__claude_ai_SEMRush__backlink_research","mcp__claude_ai_SEMRush__execute_report","mcp__claude_ai_SEMRush__get_report_schema","mcp__claude_ai_SEMRush__keyword_research","mcp__claude_ai_SEMRush__organic_research","mcp__claude_ai_SEMRush__overview_research","mcp__claude_ai_SEMRush__projects_research","mcp__claude_ai_SEMRush__siteaudit_research","mcp__claude_ai_SEMRush__subdomain_research","mcp__claude_ai_SEMRush__subfolder_research","mcp__claude_ai_SEMRush__tracking_research","mcp__claude_ai_SEMRush__trends_research","mcp__claude_ai_SEMRush__url_research","mcp__claude_ai_Vercel__add_toolbar_reaction","mcp__claude_ai_Vercel__change_toolbar_thread_resolve_status","mcp__claude_ai_Vercel__check_domain_availability_and_price","mcp__claude_ai_Vercel__deploy_to_vercel","mcp__claude_ai_Vercel__edit_toolbar_message","mcp__claude_ai_Vercel__get_access_to_vercel_url","mcp__claude_ai_Vercel__get_deployment","mcp__claude_ai_Vercel__get_deployment_build_logs","mcp__claude_ai_Vercel__get_project","mcp__claude_ai_Vercel__get_runtime_logs","mcp__claude_ai_Vercel__get_toolbar_thread","mcp__claude_ai_Vercel__list_deployments","mcp__claude_ai_Vercel__list_projects","mcp__claude_ai_Vercel__list_teams","mcp__claude_ai_Vercel__list_toolbar_threads","mcp__claude_ai_Vercel__reply_to_toolbar_thread","mcp__claude_ai_Vercel__search_vercel_documentation","mcp__claude_ai_Vercel__web_fetch_vercel_url","mcp__gpt-image-2__continue_edit_session","mcp__gpt-image-2__edit_image","mcp__gpt-image-2__end_edit_session","mcp__gpt-image-2__generate_image","mcp__gpt-image-2__list_edit_sessions","mcp__gpt-image-2__start_edit_session","mcp__hostinger__DNS_deleteDNSRecordsV1","mcp__hostinger__DNS_getDNSRecordsV1","mcp__hostinger__DNS_getDNSSnapshotListV1","mcp__hostinger__DNS_getDNSSnapshotV1","mcp__hostinger__DNS_resetDNSRecordsV1","mcp__hostinger__DNS_restoreDNSSnapshotV1","mcp__hostinger__DNS_updateDNSRecordsV1","mcp__hostinger__DNS_validateDNSRecordsV1","mcp__hostinger__VPS_activateFirewallV1","mcp__hostinger__VPS_attachPublicKeyV1","mcp__hostinger__VPS_createFirewallRuleV1","mcp__hostinger__VPS_createNewFirewallV1","mcp__hostinger__VPS_createNewProjectV1","mcp__hostinger__VPS_createPTRRecordV1","mcp__hostinger__VPS_createPostInstallScriptV1","mcp__hostinger__VPS_createPublicKeyV1","mcp__hostinger__VPS_createSnapshotV1","mcp__hostinger__VPS_deactivateFirewallV1","mcp__hostinger__VPS_deleteFirewallRuleV1","mcp__hostinger__VPS_deleteFirewallV1","mcp__hostinger__VPS_deletePTRRecordV1","mcp__hostinger__VPS_deletePostInstallScriptV1","mcp__hostinger__VPS_deleteProjectV1","mcp__hostinger__VPS_deletePublicKeyV1","mcp__hostinger__VPS_deleteSnapshotV1","mcp__hostinger__VPS_getActionDetailsV1","mcp__hostinger__VPS_getActionsV1","mcp__hostinger__VPS_getAttachedPublicKeysV1","mcp__hostinger__VPS_getBackupsV1","mcp__hostinger__VPS_getDataCenterListV1","mcp__hostinger__VPS_getFirewallDetailsV1","mcp__hostinger__VPS_getFirewallListV1","mcp__hostinger__VPS_getMetricsV1","mcp__hostinger__VPS_getPostInstallScriptV1","mcp__hostinger__VPS_getPostInstallScriptsV1","mcp__hostinger__VPS_getProjectContainersV1","mcp__hostinger__VPS_getProjectContentsV1","mcp__hostinger__VPS_getProjectListV1","mcp__hostinger__VPS_getProjectLogsV1","mcp__hostinger__VPS_getPublicKeysV1","mcp__hostinger__VPS_getScanMetricsV1","mcp__hostinger__VPS_getSnapshotV1","mcp__hostinger__VPS_getTemplateDetailsV1","mcp__hostinger__VPS_getTemplatesV1","mcp__hostinger__VPS_getVirtualMachineDetailsV1","mcp__hostinger__VPS_getVirtualMachinesV1","mcp__hostinger__VPS_installMonarxV1","mcp__hostinger__VPS_purchaseNewVirtualMachineV1","mcp__hostinger__VPS_recreateVirtualMachineV1","mcp__hostinger__VPS_resetHostnameV1","mcp__hostinger__VPS_restartProjectV1","mcp__hostinger__VPS_restartVirtualMachineV1","mcp__hostinger__VPS_restoreBackupV1","mcp__hostinger__VPS_restoreSnapshotV1","mcp__hostinger__VPS_setHostnameV1","mcp__hostinger__VPS_setNameserversV1","mcp__hostinger__VPS_setPanelPasswordV1","mcp__hostinger__VPS_setRootPasswordV1","mcp__hostinger__VPS_setupPurchasedVirtualMachineV1","mcp__hostinger__VPS_startProjectV1","mcp__hostinger__VPS_startRecoveryModeV1","mcp__hostinger__VPS_startVirtualMachineV1","mcp__hostinger__VPS_stopProjectV1","mcp__hostinger__VPS_stopRecoveryModeV1","mcp__hostinger__VPS_stopVirtualMachineV1","mcp__hostinger__VPS_syncFirewallV1","mcp__hostinger__VPS_uninstallMonarxV1","mcp__hostinger__VPS_updateFirewallRuleV1","mcp__hostinger__VPS_updatePostInstallScriptV1","mcp__hostinger__VPS_updateProjectV1","mcp__hostinger__billing_deletePaymentMethodV1","mcp__hostinger__billing_disableAutoRenewalV1","mcp__hostinger__billing_enableAutoRenewalV1","mcp__hostinger__billing_getCatalogItemListV1","mcp__hostinger__billing_getPaymentMethodListV1","mcp__hostinger__billing_getSubscriptionListV1","mcp__hostinger__billing_setDefaultPaymentMethodV1","mcp__hostinger__domains_checkDomainAvailabilityV1","mcp__hostinger__domains_createDomainForwardingV1","mcp__hostinger__domains_createWHOISProfileV1","mcp__hostinger__domains_deleteDomainForwardingV1","mcp__hostinger__domains_deleteWHOISProfileV1","mcp__hostinger__domains_disableDomainLockV1","mcp__hostinger__domains_disablePrivacyProtectionV1","mcp__hostinger__domains_enableDomainLockV1","mcp__hostinger__domains_enablePrivacyProtectionV1","mcp__hostinger__domains_getDomainDetailsV1","mcp__hostinger__domains_getDomainForwardingV1","mcp__hostinger__domains_getDomainListV1","mcp__hostinger__domains_getWHOISProfileListV1","mcp__hostinger__domains_getWHOISProfileUsageV1","mcp__hostinger__domains_getWHOISProfileV1","mcp__hostinger__domains_purchaseNewDomainV1","mcp__hostinger__domains_updateDomainNameserversV1","mcp__hostinger__hosting_createAccountDatabaseV1","mcp__hostinger__hosting_createWebsiteParkedDomainV1","mcp__hostinger__hosting_createWebsiteSubdomainV1","mcp__hostinger__hosting_createWebsiteV1","mcp__hostinger__hosting_deleteAccountDatabaseV1","mcp__hostinger__hosting_deleteWebsiteParkedDomainV1","mcp__hostinger__hosting_deleteWebsiteSubdomainV1","mcp__hostinger__hosting_deployJsApplication","mcp__hostinger__hosting_deployStaticWebsite","mcp__hostinger__hosting_deployWordpressPlugin","mcp__hostinger__hosting_deployWordpressTheme","mcp__hostinger__hosting_generateAFreeSubdomainV1","mcp__hostinger__hosting_importWordpressWebsite","mcp__hostinger__hosting_installWordPressV1","mcp__hostinger__hosting_listAccountDatabasesV1","mcp__hostinger__hosting_listAvailableDatacentersV1","mcp__hostinger__hosting_listJsDeployments","mcp__hostinger__hosting_listOrdersV1","mcp__hostinger__hosting_listWebsiteParkedDomainsV1","mcp__hostinger__hosting_listWebsiteSubdomainsV1","mcp__hostinger__hosting_listWebsitesV1","mcp__hostinger__hosting_listWordPressInstallationsV1","mcp__hostinger__hosting_showJsDeploymentLogs","mcp__hostinger__hosting_verifyDomainOwnershipV1","mcp__hostinger__reach_createANewContactSegmentV1","mcp__hostinger__reach_createANewContactV1","mcp__hostinger__reach_createNewContactsV1","mcp__hostinger__reach_deleteAContactV1","mcp__hostinger__reach_getSegmentDetailsV1","mcp__hostinger__reach_listContactGroupsV1","mcp__hostinger__reach_listContactsV1","mcp__hostinger__reach_listProfilesV1","mcp__hostinger__reach_listSegmentContactsV1","mcp__hostinger__reach_listSegmentsV1","mcp__hostinger__v2_getDomainVerificationsDIRECT","mcp__n8n-mcp__get_node","mcp__n8n-mcp__get_template","mcp__n8n-mcp__n8n_audit_instance","mcp__n8n-mcp__n8n_autofix_workflow","mcp__n8n-mcp__n8n_create_workflow","mcp__n8n-mcp__n8n_delete_workflow","mcp__n8n-mcp__n8n_deploy_template","mcp__n8n-mcp__n8n_executions","mcp__n8n-mcp__n8n_generate_workflow","mcp__n8n-mcp__n8n_get_workflow","mcp__n8n-mcp__n8n_health_check","mcp__n8n-mcp__n8n_list_workflows","mcp__n8n-mcp__n8n_manage_credentials","mcp__n8n-mcp__n8n_manage_datatable","mcp__n8n-mcp__n8n_test_workflow","mcp__n8n-mcp__n8n_update_full_workflow","mcp__n8n-mcp__n8n_update_partial_workflow","mcp__n8n-mcp__n8n_validate_workflow","mcp__n8n-mcp__n8n_workflow_versions","mcp__n8n-mcp__search_nodes","mcp__n8n-mcp__search_templates","mcp__n8n-mcp__tools_documentation","mcp__n8n-mcp__validate_node","mcp__n8n-mcp__validate_workflow","mcp__nanobanana__generate_image","mcp__nanobanana__maintenance","mcp__nanobanana__show_output_stats","mcp__nanobanana__upload_file","mcp__plugin_vercel_vercel__authenticate","mcp__plugin_vercel_vercel__complete_authentication","mcp__refero__refero_get_flow","mcp__refero__refero_get_screen","mcp__refero__refero_get_screen_image","mcp__refero__refero_get_similar_screens","mcp__refero__refero_get_style","mcp__refero__refero_search_flows","mcp__refero__refero_search_screens","mcp__refero__refero_search_styles","mcp__semrush__authenticate","mcp__semrush__complete_authentication","mcp__stock-images-mcp__search_stock_images","mcp__trello__add_card_to_list","mcp__trello__add_checklist_item","mcp__trello__add_comment","mcp__trello__add_list_to_board","mcp__trello__archive_card","mcp__trello__archive_list","mcp__trello__assign_member_to_card","mcp__trello__attach_file_to_card","mcp__trello__attach_image_data_to_card","mcp__trello__attach_image_to_card","mcp__trello__create_board","mcp__trello__create_checklist","mcp__trello__create_label","mcp__trello__delete_comment","mcp__trello__delete_label","mcp__trello__find_checklist_items_by_description","mcp__trello__get_acceptance_criteria","mcp__trello__get_active_board_info","mcp__trello__get_board_labels","mcp__trello__get_board_members","mcp__trello__get_card","mcp__trello__get_card_comments","mcp__trello__get_card_history","mcp__trello__get_cards_by_list_id","mcp__trello__get_checklist_by_name","mcp__trello__get_checklist_items","mcp__trello__get_health","mcp__trello__get_health_detailed","mcp__trello__get_health_metadata","mcp__trello__get_health_performance","mcp__trello__get_lists","mcp__trello__get_my_cards","mcp__trello__get_recent_activity","mcp__trello__list_boards","mcp__trello__list_boards_in_workspace","mcp__trello__list_workspaces","mcp__trello__move_card","mcp__trello__perform_system_repair","mcp__trello__remove_member_from_card","mcp__trello__set_active_board","mcp__trello__set_active_workspace","mcp__trello__update_card_details","mcp__trello__update_checklist_item","mcp__trello__update_comment","mcp__trello__update_label"],"removedNames":[],"readdedNames":[]},"type":"attachment","uuid":"b5ac15e2-b1b0-4b51-8c17-2eb5b012775f","timestamp":"2026-06-14T16:48:01.241Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"b5ac15e2-b1b0-4b51-8c17-2eb5b012775f","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","attachment":{"type":"skill_listing","content":"- adapt\n- animate\n- arrange\n- audit\n- bolder\n- bun-to-next\n- clarify\n- colorize\n- critique\n- delight\n- design-taste-frontend: Anti-slop frontend skill for landing pages, portfolios, and redesigns. The agent reads the brief, infers the right design direction, and ships interfaces that do not look templated. Real design systems when applicable, audit-first on redesigns, strict pre-flight check.\n- distill\n- extract\n- feedbackpin: Fetch visual feedback tasks from FeedbackPin, resolve them by fixing the code, and report back via API\n- full-output-enforcement\n- gsap-core\n- gsap-frameworks\n- gsap-performance\n- gsap-plugins\n- gsap-react\n- gsap-scrolltrigger\n- gsap-timeline\n- gsap-utils\n- harden\n- high-end-visual-design\n- impeccable\n- industrial-brutalist-ui\n- minimalist-ui\n- netlify-cli\n- nextjs-wordpress-headless: Branche un projet Next.js (App Router) sur un WordPress headless auto-hébergé (Docker sur VPS Ubuntu, ACF PRO Flexible Content + WPGraphQL) avec un catalogue de blocks réorganisables, revalidation par tags et seed idempotent. Gère pages one-off, CPT, options globales, SEO.\n- normalize\n- onboard\n- optimize\n- overdrive\n- paf-site-magic\n- polish\n- quieter\n- redesign-existing-projects\n- refero-design\n- scheduled-actions\n- seo\n- seo-audit\n- seo-backlinks\n- seo-competitor-pages\n- seo-content\n- seo-dataforseo\n- seo-firecrawl\n- seo-geo\n- seo-google\n- seo-hreflang\n- seo-image-gen\n- seo-images\n- seo-local\n- seo-maps\n- seo-page\n- seo-plan\n- seo-programmatic\n- seo-schema\n- seo-sitemap\n- seo-technical\n- shape\n- stitch-design-taste\n- typeset\n- brainstorming\n- brutalist-skill\n- check-conflicts: Verifie les conflits potentiels avec les PRs ouvertes avant de travailler sur un fichier ou une section.\n- dispatching-parallel-agents\n- executing-plans\n- feedbackpin: Fetch visual feedback tasks from FeedbackPin, resolve them by fixing the code, and report back via API\n- finishing-a-development-branch\n- minimalist-skill\n- output-skill\n- receiving-code-review\n- redesign-skill\n- requesting-code-review\n- soft-skill\n- stitch-skill\n- subagent-driven-development\n- systematic-debugging\n- taste-skill\n- test-driven-development\n- using-git-worktrees\n- using-superpowers\n- verification-before-completion\n- writing-plans: Use when you have a spec or requirements for a multi-step task, before touching code\n- writing-skills\n- handoff: Cree ou met a jour HANDOFF.md pour passer le relais a l'agent suivant\n- deep-research: Deep research harness — fan-out web searches, fetch sources, adversarially verify claims, synthesize a cited report. - When the user wants a deep, multi-source, fact-checked research report on any topic. BEFORE invoking, check if the question is specific enough to research directly — if underspecified (e.g., \"what car to buy\" without budget/use-case/region), ask 2-3 clarifying questions to narrow scope. Then pass the refined question as args, weaving the answers in.\n- vercel:bootstrap\n- vercel:deploy\n- vercel:env\n- vercel:marketplace\n- vercel:status\n- vercel:ai-gateway\n- vercel:ai-sdk\n- vercel:auth\n- vercel:bootstrap\n- vercel:chat-sdk\n- vercel:deployments-cicd\n- vercel:env-vars\n- vercel:knowledge-update\n- vercel:marketplace\n- vercel:next-cache-components\n- vercel:next-forge\n- vercel:next-upgrade\n- vercel:nextjs\n- vercel:react-best-practices\n- vercel:routing-middleware\n- vercel:runtime-cache\n- vercel:shadcn\n- vercel:turbopack\n- vercel:vercel-agent\n- vercel:vercel-cli\n- vercel:vercel-firewall\n- vercel:vercel-functions\n- vercel:vercel-sandbox\n- vercel:vercel-storage\n- vercel:verification\n- vercel:workflow: Vercel Workflow DevKit (WDK) expert guidance. Use when building durable workflows, long-running tasks, API routes or agents that need pause/resume, retries, step-based execution, or crash-safe orchestration with Vercel Workflow.\n- update-config: Use this skill to configure the Claude Code harness via settings.json. Automated behaviors (\"from now on when X\", \"each time X\", \"whenever X\", \"before/after X\") require hooks configured in settings.json - the harness executes these, not Claude, so memory/preferences cannot fulfill them. Also use for: permissions (\"allow X\", \"add permission\", \"move permission to\"), env vars (\"set X=Y\"), hook troubleshooting, or any changes to settings.json/settings.local.json files. Examples: \"allow npm commands\", \"add bq permission to global settings\", \"move permission to user settings\", \"set DEBUG=true\", \"when claude stops show X\". For simple settings like theme/model, suggest the /config command.\n- keybindings-help: Use when the user wants to customize keyboard shortcuts, rebind keys, add chord bindings, or modify ~/.claude/keybindings.json. Examples: \"rebind ctrl+s\", \"add a chord shortcut\", \"change the submit key\", \"customize keybindings\".\n- verify: Verify that a code change actually does what it's supposed to by running the app and observing behavior. Use when asked to verify a PR, confirm a fix works, test a change manually, check that a feature works, or validate local changes before pushing.\n- code-review: Review the current diff for correctness bugs and reuse/simplification/efficiency cleanups at the given effort level (low/medium: fewer, high-confidence findings; high→max: broader coverage, may include uncertain findings; ultra: deep multi-agent review in the cloud). Pass --comment to post findings as inline PR comments, or --fix to apply the findings to the working tree after the review.\n- simplify: Review the changed code for reuse, simplification, efficiency, and altitude cleanups, then apply the fixes. Quality only — it does not hunt for bugs; use /code-review for that.\n- fewer-permission-prompts: Scan your transcripts for common read-only Bash and MCP tool calls, then add a prioritized allowlist to project .claude/settings.json to reduce permission prompts.\n- loop: Run a prompt or slash command on a recurring interval (e.g. /loop 5m /foo). Omit the interval to let the model self-pace. - When the user wants to set up a recurring task, poll for status, or run something repeatedly on an interval (e.g. \"check the deploy every 5 minutes\", \"keep running /babysit-prs\"). Do NOT invoke for one-off tasks.\n- schedule: Create, update, list, or run scheduled cloud agents (routines) that execute on a cron schedule. - When the user wants to schedule a recurring cloud agent, set up automated tasks, create a cron job for Claude Code, or manage their scheduled agents/routines. Also use when the user wants a one-time scheduled run (\"run this once at 3pm\", \"remind me to check X tomorrow\").\n- claude-api: Reference for the Claude API / Anthropic SDK — model ids, pricing, params, streaming, tool use, MCP, agents, caching, token counting, model migration.\nTRIGGER — read BEFORE opening the target file; don't skip because it \"looks like a one-liner\" — whenever: the prompt names Claude/Anthropic in any form (Claude, Anthropic, Fable, Opus, Sonnet, Haiku, `anthropic`, `@anthropic-ai`, `claude-*`, `us.anthropic.*`, `[1m]`); the user asks about an LLM (pricing/model choice/limits/caching) — never answer from memory; OR the task is LLM-shaped with provider unstated (agent/MCP/tool-definition/multi-agent/RAG/LLM-judge/computer-use; generate/summarize/extract/classify/rewrite/converse over NL; debugging refusals/cutoffs/streaming/tool-calls/tokens).\nSKIP only when another provider is being worked on (overrides all triggers): OpenAI/GPT/Gemini/Llama/Mistral/Cohere/Ollama named in the query; OR `grep -rE 'openai|langchain_openai|google.generativeai|genai|mistralai|cohere|ollama'` over the project hits (run this grep FIRST if no provider named — don't Read the file).\n- run: Launch and drive this project's app to see a change working. Use when asked to run, start, or screenshot the app, or to confirm a change works in the real app (not just tests). First looks for a project skill that already covers launching the app; otherwise falls back to built-in patterns per project type (CLI, server, TUI, Electron, browser-driven, library).\n- init\n- review\n- security-review","skillCount":132,"isInitial":true,"names":["adapt","animate","arrange","audit","bolder","bun-to-next","clarify","colorize","critique","delight","design-taste-frontend","distill","extract","feedbackpin","full-output-enforcement","gsap-core","gsap-frameworks","gsap-performance","gsap-plugins","gsap-react","gsap-scrolltrigger","gsap-timeline","gsap-utils","harden","high-end-visual-design","impeccable","industrial-brutalist-ui","minimalist-ui","netlify-cli","nextjs-wordpress-headless","normalize","onboard","optimize","overdrive","paf-site-magic","polish","quieter","redesign-existing-projects","refero-design","scheduled-actions","seo","seo-audit","seo-backlinks","seo-competitor-pages","seo-content","seo-dataforseo","seo-firecrawl","seo-geo","seo-google","seo-hreflang","seo-image-gen","seo-images","seo-local","seo-maps","seo-page","seo-plan","seo-programmatic","seo-schema","seo-sitemap","seo-technical","shape","stitch-design-taste","typeset","brainstorming","brutalist-skill","check-conflicts","dispatching-parallel-agents","executing-plans","feedbackpin","finishing-a-development-branch","minimalist-skill","output-skill","receiving-code-review","redesign-skill","requesting-code-review","soft-skill","stitch-skill","subagent-driven-development","systematic-debugging","taste-skill","test-driven-development","using-git-worktrees","using-superpowers","verification-before-completion","writing-plans","writing-skills","handoff","deep-research","vercel:bootstrap","vercel:deploy","vercel:env","vercel:marketplace","vercel:status","vercel:ai-gateway","vercel:ai-sdk","vercel:auth","vercel:bootstrap","vercel:chat-sdk","vercel:deployments-cicd","vercel:env-vars","vercel:knowledge-update","vercel:marketplace","vercel:next-cache-components","vercel:next-forge","vercel:next-upgrade","vercel:nextjs","vercel:react-best-practices","vercel:routing-middleware","vercel:runtime-cache","vercel:shadcn","vercel:turbopack","vercel:vercel-agent","vercel:vercel-cli","vercel:vercel-firewall","vercel:vercel-functions","vercel:vercel-sandbox","vercel:vercel-storage","vercel:verification","vercel:workflow","update-config","keybindings-help","verify","code-review","simplify","fewer-permission-prompts","loop","schedule","claude-api","run","init","review","security-review"]},"type":"attachment","uuid":"80ad0024-72ed-4007-a3e4-211871bb5092","timestamp":"2026-06-14T16:48:01.244Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"80ad0024-72ed-4007-a3e4-211871bb5092","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_01EoRofK7ECXyaZYS2LHWdnr","type":"message","role":"assistant","content":[{"type":"text","text":"I'll investigate the \"Composant d'origine\" / \"origine registry\" feature systematically. Let me start by searching for key terms and file structure."}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":23293,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":23293,"ephemeral_1h_input_tokens":0},"output_tokens":8,"service_tier":"standard","inference_geo":"not_available"},"diagnostics":null},"requestId":"req_011Cc3Vaf674qf8kfuMnY25S","attributionAgent":"Explore","type":"assistant","uuid":"cff33834-c454-4d5c-8fe5-c3abce8026f5","timestamp":"2026-06-14T16:48:02.939Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"cff33834-c454-4d5c-8fe5-c3abce8026f5","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_01EoRofK7ECXyaZYS2LHWdnr","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01JyyfwWDDda5kShDKPtk3JY","name":"Bash","input":{"command":"find /opt/projects/.covalba-wp-worktrees/tom-passage-wp -type f \\( -name \"*.ts\" -o -name \"*.tsx\" -o -name \"*.js\" -o -name \"*.json\" -o -name \"*.php\" \\) | head -20"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":23293,"cache_read_input_tokens":0,"output_tokens":155,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":23293},"inference_geo":"not_available","iterations":[{"input_tokens":3,"output_tokens":155,"cache_read_input_tokens":0,"cache_creation_input_tokens":23293,"cache_creation":{"ephemeral_5m_input_tokens":23293,"ephemeral_1h_input_tokens":0},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011Cc3Vaf674qf8kfuMnY25S","attributionAgent":"Explore","type":"assistant","uuid":"e62d1785-c8f7-473d-946b-e1e4437fdd0b","timestamp":"2026-06-14T16:48:03.205Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"e62d1785-c8f7-473d-946b-e1e4437fdd0b","isSidechain":true,"promptId":"7f3e3cb7-906e-4e3a-92fd-290df6bb8908","agentId":"a80d1ba9a1945c3c3","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01JyyfwWDDda5kShDKPtk3JY","type":"tool_result","content":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp/postcss.config.js\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/package.json\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/vitest.config.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/vercel.json\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/vite.config.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/tailwind.config.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/eslint.config.js\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/next-env.d.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/package-lock.json\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/tsconfig.json\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/next.config.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/components.json\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/app/providers.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/app/sitemap.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/app/page.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/app/not-found.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/app/layout.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/.claude/launch.json\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/.next/build-manifest.json\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/.next/package.json","is_error":false}]},"uuid":"a57e0e3b-74c6-447a-9590-2c89f964572e","timestamp":"2026-06-14T16:48:03.310Z","sourceToolAssistantUUID":"e62d1785-c8f7-473d-946b-e1e4437fdd0b","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"a57e0e3b-74c6-447a-9590-2c89f964572e","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_01McdG51iLBBkTNSHyBfhDBK","type":"message","role":"assistant","content":[{"type":"text","text":"Let me search for the key terms across the codebase."}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":5,"cache_creation_input_tokens":2254,"cache_read_input_tokens":23293,"cache_creation":{"ephemeral_5m_input_tokens":2254,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"diagnostics":null},"requestId":"req_011Cc3VaovRRyXPvDATXFkMA","attributionAgent":"Explore","type":"assistant","uuid":"112e6b26-883d-48a4-9e00-0f65dc0623d2","timestamp":"2026-06-14T16:48:05.775Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"112e6b26-883d-48a4-9e00-0f65dc0623d2","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_01McdG51iLBBkTNSHyBfhDBK","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01XxgtVysCQAWez665TM8EeL","name":"Bash","input":{"command":"find /opt/projects/.covalba-wp-worktrees/tom-passage-wp -type f \\( -name \"*.ts\" -o -name \"*.tsx\" -o -name \"*.php\" \\) | xargs grep -l \"origine\\|Origine\\|composant_react\\|ComposantReact\\|originRegistry\\|OriginRegistry\" 2>/dev/null"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":5,"cache_creation_input_tokens":2254,"cache_read_input_tokens":23293,"output_tokens":157,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":2254},"inference_geo":"not_available","iterations":[{"input_tokens":5,"output_tokens":157,"cache_read_input_tokens":23293,"cache_creation_input_tokens":2254,"cache_creation":{"ephemeral_5m_input_tokens":2254,"ephemeral_1h_input_tokens":0},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011Cc3VaovRRyXPvDATXFkMA","attributionAgent":"Explore","type":"assistant","uuid":"98275b6e-b0d0-4f34-9f31-317ac58e756d","timestamp":"2026-06-14T16:48:06.672Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"98275b6e-b0d0-4f34-9f31-317ac58e756d","isSidechain":true,"promptId":"7f3e3cb7-906e-4e3a-92fd-290df6bb8908","agentId":"a80d1ba9a1945c3c3","type":"user","message":{"role":"user","content":[{"type":"tool_result","content":"Exit code 123\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/views/DevenirApplicateur.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/views/Estimation.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/views/Diagnostic.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/views/QuiSommesNous.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/data/roofPages.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/mappers.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/toCustomerReference.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/toSectorConfig.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/toRoofPageData.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/types.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/origine/produits.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/origine/index.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/origine/home.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/origine/oneoff.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/origine/industrie.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/blocks/ComposantReact.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/blocks/shared.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/blocks/BlockRenderer.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/product/CoolRoofGuide.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/app/toitures/[slug]/page.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/app/industries/[slug]/sector-view-client.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/app/industries/[slug]/page.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/viewProps/simulateurEconomieEnergie.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/viewProps/contact.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/viewProps/shared.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/viewProps/devenirApplicateur.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/viewProps/primeCEECoolRoof.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/viewProps/guideCoolRoof.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/etapes.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/faq.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/composantReact.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/avantApres.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/index.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/variantesProduit.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/timeline.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/logos.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/referencesGrille.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/ficheChantier.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/barreReassurance.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/cta.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/temoignagesVideo.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/texteImage.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/grilleSecteurs.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/tableauSituations.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/chiffres.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/compatibiliteSupports.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/grilleSolutions.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/grilleCards.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/specsTechniques.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/contenuSeo.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/certifications.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/video.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/citation.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/hero.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/tableauComparatif.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/product/covaseal/CovaSealShowcase.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/product/covaseal/CovaSealEditorial.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/product/covatherm-light/CovaThermLightEditorial.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/product/covatherm-light/CovaThermLightShowcase.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/product/covametal/CovaMetalEditorial.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/product/covametal/CovaMetalShowcase.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/helpers.php\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/composant-react.php","is_error":true,"tool_use_id":"toolu_01XxgtVysCQAWez665TM8EeL"}]},"uuid":"673bc33d-214c-4c57-8738-b83e467a6150","timestamp":"2026-06-14T16:48:08.656Z","toolUseResult":"Error: Exit code 123\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/views/DevenirApplicateur.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/views/Estimation.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/views/Diagnostic.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/views/QuiSommesNous.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/data/roofPages.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/mappers.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/toCustomerReference.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/toSectorConfig.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/toRoofPageData.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/types.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/origine/produits.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/origine/index.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/origine/home.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/origine/oneoff.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/origine/industrie.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/blocks/ComposantReact.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/blocks/shared.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/blocks/BlockRenderer.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/product/CoolRoofGuide.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/app/toitures/[slug]/page.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/app/industries/[slug]/sector-view-client.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/app/industries/[slug]/page.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/viewProps/simulateurEconomieEnergie.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/viewProps/contact.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/viewProps/shared.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/viewProps/devenirApplicateur.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/viewProps/primeCEECoolRoof.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/viewProps/guideCoolRoof.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/etapes.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/faq.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/composantReact.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/avantApres.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/index.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/variantesProduit.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/timeline.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/logos.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/referencesGrille.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/ficheChantier.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/barreReassurance.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/cta.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/temoignagesVideo.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/texteImage.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/grilleSecteurs.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/tableauSituations.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/chiffres.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/compatibiliteSupports.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/grilleSolutions.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/grilleCards.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/specsTechniques.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/contenuSeo.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/certifications.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/video.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/citation.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/hero.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/tableauComparatif.ts\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/product/covaseal/CovaSealShowcase.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/product/covaseal/CovaSealEditorial.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/product/covatherm-light/CovaThermLightEditorial.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/product/covatherm-light/CovaThermLightShowcase.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/product/covametal/CovaMetalEditorial.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/product/covametal/CovaMetalShowcase.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/helpers.php\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/composant-react.php","sourceToolAssistantUUID":"98275b6e-b0d0-4f34-9f31-317ac58e756d","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"673bc33d-214c-4c57-8738-b83e467a6150","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_01G8JsiXt7RUxVQeYJuB9eae","type":"message","role":"assistant","content":[{"type":"text","text":"Good. Let me now read the key files:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":6,"cache_creation_input_tokens":2425,"cache_read_input_tokens":25547,"cache_creation":{"ephemeral_5m_input_tokens":2425,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"diagnostics":null},"requestId":"req_011Cc3VbD4DHqmxEfc53ZEcV","attributionAgent":"Explore","type":"assistant","uuid":"bc0eb1a1-0d63-47fd-85b5-0f7d2c8e2616","timestamp":"2026-06-14T16:48:10.266Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"bc0eb1a1-0d63-47fd-85b5-0f7d2c8e2616","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_01G8JsiXt7RUxVQeYJuB9eae","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01JbREaZCqKhGThqfiVbMYDg","name":"Read","input":{"file_path":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/origine/index.ts"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":6,"cache_creation_input_tokens":2425,"cache_read_input_tokens":25547,"cache_creation":{"ephemeral_5m_input_tokens":2425,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"diagnostics":null},"requestId":"req_011Cc3VbD4DHqmxEfc53ZEcV","attributionAgent":"Explore","type":"assistant","uuid":"3c79032d-8a73-4994-9a6b-eefe7cd67061","timestamp":"2026-06-14T16:48:10.479Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"3c79032d-8a73-4994-9a6b-eefe7cd67061","isSidechain":true,"promptId":"7f3e3cb7-906e-4e3a-92fd-290df6bb8908","agentId":"a80d1ba9a1945c3c3","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01JbREaZCqKhGThqfiVbMYDg","type":"tool_result","content":"1\t// Registry des composants d'ORIGINE du site, keyé par la valeur du champ\n2\t// `origine` des sections WordPress. Chaque entrée est un adaptateur qui mappe\n3\t// les données WP vers les props du composant original (src/components/*,\n4\t// src/views/*) — JAMAIS une réimplémentation. Un fichier par famille de pages\n5\t// pour permettre le travail en parallèle.\n6\timport type { ComponentType } from \"react\";\n7\timport { origineHome } from \"./home\";\n8\timport { origineProduits } from \"./produits\";\n9\timport { origineIndustrie } from \"./industrie\";\n10\timport { origineOneoff } from \"./oneoff\";\n11\t\n12\texport const origineRegistry: Record<string, ComponentType<any>> = {\n13\t  ...origineHome,\n14\t  ...origineProduits,\n15\t  ...origineIndustrie,\n16\t  ...origineOneoff,\n17\t};\n18\t"}]},"uuid":"87066b59-602e-4426-923d-e849955dc8b3","timestamp":"2026-06-14T16:48:10.512Z","sourceToolAssistantUUID":"3c79032d-8a73-4994-9a6b-eefe7cd67061","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"87066b59-602e-4426-923d-e849955dc8b3","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_01G8JsiXt7RUxVQeYJuB9eae","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01UeXc8DszeQaXatSQa8fpaC","name":"Read","input":{"file_path":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/blocks/BlockRenderer.tsx"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":6,"cache_creation_input_tokens":2425,"cache_read_input_tokens":25547,"cache_creation":{"ephemeral_5m_input_tokens":2425,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"diagnostics":null},"requestId":"req_011Cc3VbD4DHqmxEfc53ZEcV","attributionAgent":"Explore","type":"assistant","uuid":"92c226de-2f3c-407a-84ea-c48ad63fa42c","timestamp":"2026-06-14T16:48:10.724Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"92c226de-2f3c-407a-84ea-c48ad63fa42c","isSidechain":true,"promptId":"7f3e3cb7-906e-4e3a-92fd-290df6bb8908","agentId":"a80d1ba9a1945c3c3","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01UeXc8DszeQaXatSQa8fpaC","type":"tool_result","content":"1\t\"use client\";\n2\t\n3\t// Rendu des sections WordPress (flexible content `sections`).\n4\t// Frontière CLIENT : les composants d'origine (et leurs exports de défauts)\n5\t// sont des modules \"use client\" — les consommer depuis du code serveur en\n6\t// ferait des références client inertes. Le renderer reçoit les sections en\n7\t// JSON sérialisable depuis les routes serveur, comme la SPA d'origine. Priorité au registry `origine` : chaque section seedée\n8\t// porte le nom du composant React d'ORIGINE du site (parité pixel avec la\n9\t// version pré-CMS) — l'adaptateur mappe les données WP vers les props du\n10\t// composant original. Fallback : block générique matché sur le suffixe\n11\t// `...XxxLayout` du `__typename` (sections ajoutées dans le BO sans origine).\n12\t\n13\timport type { ComponentType } from \"react\";\n14\timport type { WpSection } from \"@/lib/wp/types\";\n15\timport { origineRegistry } from \"@/components/origine\";\n16\t\n17\timport Hero from \"./Hero\";\n18\timport BarreReassurance from \"./BarreReassurance\";\n19\timport GrilleCards from \"./GrilleCards\";\n20\timport TexteImage from \"./TexteImage\";\n21\timport Chiffres from \"./Chiffres\";\n22\timport TableauComparatif from \"./TableauComparatif\";\n23\timport TableauSituations from \"./TableauSituations\";\n24\timport Etapes from \"./Etapes\";\n25\timport Faq from \"./Faq\";\n26\timport Cta from \"./Cta\";\n27\timport TemoignagesVideo from \"./TemoignagesVideo\";\n28\timport Video from \"./Video\";\n29\timport Logos from \"./Logos\";\n30\timport Citation from \"./Citation\";\n31\timport ContenuSeo from \"./ContenuSeo\";\n32\timport CompatibiliteSupports from \"./CompatibiliteSupports\";\n33\timport GrilleSecteurs from \"./GrilleSecteurs\";\n34\timport GrilleSolutions from \"./GrilleSolutions\";\n35\timport ReferencesGrille from \"./ReferencesGrille\";\n36\timport Timeline from \"./Timeline\";\n37\timport ComposantReact from \"./ComposantReact\";\n38\timport SpecsTechniques from \"./SpecsTechniques\";\n39\timport Certifications from \"./Certifications\";\n40\timport VariantesProduit from \"./VariantesProduit\";\n41\timport AvantApres from \"./AvantApres\";\n42\timport FicheChantier from \"./FicheChantier\";\n43\t\n44\tconst registry: Record<string, ComponentType<any>> = {\n45\t  HeroLayout: Hero,\n46\t  BarreReassuranceLayout: BarreReassurance,\n47\t  GrilleCardsLayout: GrilleCards,\n48\t  TexteImageLayout: TexteImage,\n49\t  ChiffresLayout: Chiffres,\n50\t  TableauComparatifLayout: TableauComparatif,\n51\t  TableauSituationsLayout: TableauSituations,\n52\t  EtapesLayout: Etapes,\n53\t  FaqLayout: Faq,\n54\t  CtaLayout: Cta,\n55\t  TemoignagesVideoLayout: TemoignagesVideo,\n56\t  VideoLayout: Video,\n57\t  LogosLayout: Logos,\n58\t  CitationLayout: Citation,\n59\t  ContenuSeoLayout: ContenuSeo,\n60\t  CompatibiliteSupportsLayout: CompatibiliteSupports,\n61\t  GrilleSecteursLayout: GrilleSecteurs,\n62\t  GrilleSolutionsLayout: GrilleSolutions,\n63\t  ReferencesGrilleLayout: ReferencesGrille,\n64\t  TimelineLayout: Timeline,\n65\t  ComposantReactLayout: ComposantReact,\n66\t  SpecsTechniquesLayout: SpecsTechniques,\n67\t  CertificationsLayout: Certifications,\n68\t  VariantesProduitLayout: VariantesProduit,\n69\t  AvantApresLayout: AvantApres,\n70\t  FicheChantierLayout: FicheChantier,\n71\t};\n72\t\n73\t// Tri par longueur décroissante : `TemoignagesVideoLayout` doit matcher avant\n74\t// `VideoLayout` (les deux sont des suffixes valides du même __typename).\n75\tconst registryKeys = Object.keys(registry).sort((a, b) => b.length - a.length);\n76\t\n77\texport function BlockRenderer({\n78\t  sections,\n79\t}: {\n80\t  sections: WpSection[] | null | undefined;\n81\t}) {\n82\t  if (!sections?.length) return null;\n83\t\n84\t  return (\n85\t    <>\n86\t      {sections.map((section, i) => {\n87\t        const typename = section?.__typename;\n88\t        if (!typename) return null;\n89\t\n90\t        // 1) Composant d'origine du site (parité pixel) — un adaptateur par\n91\t        //    valeur d'`origine`, qui rend le composant original paramétré.\n92\t        //    Sentinel \"_absorbe\" : row dont les données sont consommées par\n93\t        //    l'adaptateur d'une row voisine (composants 1-pour-N) — pas de rendu.\n94\t        const origine = section.origine;\n95\t        if (origine === \"_absorbe\") return null;\n96\t        if (origine && origineRegistry[origine]) {\n97\t          const Original = origineRegistry[origine];\n98\t          return <Original key={i} {...section} position={i} />;\n99\t        }\n100\t        if (origine && process.env.NODE_ENV !== \"production\") {\n101\t          console.warn(\n102\t            `[BlockRenderer] origine \"${origine}\" sans adaptateur — fallback générique`,\n103\t          );\n104\t        }\n105\t\n106\t        // 2) Fallback générique par layout.\n107\t        const key = registryKeys.find((k) => typename.endsWith(k));\n108\t        if (!key) {\n109\t          if (process.env.NODE_ENV !== \"production\") {\n110\t            console.warn(`[BlockRenderer] Layout inconnu : ${typename}`);\n111\t          }\n112\t          return null;\n113\t        }\n114\t\n115\t        const Block = registry[key];\n116\t        return <Block key={i} {...section} position={i} />;\n117\t      })}\n118\t    </>\n119\t  );\n120\t}\n121\t\n122\texport default BlockRenderer;\n123\t"}]},"uuid":"0293970f-1c0a-445b-ba10-166c4a6b325b","timestamp":"2026-06-14T16:48:10.777Z","sourceToolAssistantUUID":"92c226de-2f3c-407a-84ea-c48ad63fa42c","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"0293970f-1c0a-445b-ba10-166c4a6b325b","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_01G8JsiXt7RUxVQeYJuB9eae","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01FgU3z2ZUWFciebjpzz9ij8","name":"Read","input":{"file_path":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/blocks/ComposantReact.tsx"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":6,"cache_creation_input_tokens":2425,"cache_read_input_tokens":25547,"cache_creation":{"ephemeral_5m_input_tokens":2425,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"diagnostics":null},"requestId":"req_011Cc3VbD4DHqmxEfc53ZEcV","attributionAgent":"Explore","type":"assistant","uuid":"31f16ebf-7652-4236-bafb-e425a89cc1ec","timestamp":"2026-06-14T16:48:11.168Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"31f16ebf-7652-4236-bafb-e425a89cc1ec","isSidechain":true,"promptId":"7f3e3cb7-906e-4e3a-92fd-290df6bb8908","agentId":"a80d1ba9a1945c3c3","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01FgU3z2ZUWFciebjpzz9ij8","type":"tool_result","content":"1\t\"use client\";\n2\t\n3\t// Block `composant_react` — escape hatch : monte un composant interactif codé\n4\t// côté front. Branché : formulaire_hubspot (HubSpotForm), applicateur_screener\n5\t// (ApplicateurScreener), diagnostic_toiture (DiagnosticForm). Les autres\n6\t// (simulateur_energie, estimation, climate_dashboard, carte_intervention)\n7\t// rendent un placeholder propre en attendant un montage dédié.\n8\t\n9\timport HubSpotForm from \"@/components/HubSpotForm\";\n10\timport { ApplicateurScreener } from \"@/components/applicateur/ApplicateurScreener\";\n11\timport { DiagnosticForm } from \"@/components/diagnostic/DiagnosticForm\";\n12\timport type { ComposantReactBlock } from \"@/lib/wp/types\";\n13\t\n14\tconst Placeholder = ({ composant }: { composant: string }) => (\n15\t  <div\n16\t    data-component={composant}\n17\t    className=\"rounded-2xl border border-dashed border-foreground/20 bg-muted/30 p-10 text-center\"\n18\t  >\n19\t    <p className=\"font-satoshi text-base font-bold text-foreground/60\">\n20\t      Composant interactif « {composant} »\n21\t    </p>\n22\t    <p className=\"mt-2 font-body text-sm text-foreground/40\">\n23\t      Ce module sera monté côté front (non disponible dans ce contexte).\n24\t    </p>\n25\t  </div>\n26\t);\n27\t\n28\tconst ComposantReact = (props: ComposantReactBlock & { position?: number }) => {\n29\t  const composant = props.composant ?? \"\";\n30\t  if (!composant) return null;\n31\t\n32\t  let inner: React.ReactNode;\n33\t  switch (composant) {\n34\t    case \"formulaire_hubspot\":\n35\t      inner = props.hubspotFormId ? (\n36\t        <div className=\"max-w-2xl mx-auto rounded-2xl border border-border bg-white p-6 lg:p-8 shadow-[0_2px_20px_rgba(26,42,58,0.05)]\">\n37\t          <HubSpotForm\n38\t            targetId={`hubspot-block-${props.position ?? 0}`}\n39\t            formId={props.hubspotFormId}\n40\t          />\n41\t        </div>\n42\t      ) : (\n43\t        <Placeholder composant={composant} />\n44\t      );\n45\t      break;\n46\t    case \"applicateur_screener\":\n47\t      inner = <ApplicateurScreener />;\n48\t      break;\n49\t    case \"diagnostic_toiture\":\n50\t      inner = <DiagnosticForm />;\n51\t      break;\n52\t    default:\n53\t      inner = <Placeholder composant={composant} />;\n54\t  }\n55\t\n56\t  return (\n57\t    <section className=\"py-16 lg:py-24 bg-background\">\n58\t      <div className=\"container mx-auto px-4 lg:px-8\">\n59\t        {props.titre && (\n60\t          <h2\n61\t            className=\"font-satoshi text-3xl sm:text-4xl font-black text-foreground !leading-[1.05] text-center mb-10\"\n62\t            style={{ letterSpacing: \"-0.03em\" }}\n63\t          >\n64\t            {props.titre}\n65\t          </h2>\n66\t        )}\n67\t        {inner}\n68\t      </div>\n69\t    </section>\n70\t  );\n71\t};\n72\t\n73\texport default ComposantReact;\n74\t"}]},"uuid":"cc157733-011b-44e0-9cb7-06af4936fcb0","timestamp":"2026-06-14T16:48:11.186Z","sourceToolAssistantUUID":"31f16ebf-7652-4236-bafb-e425a89cc1ec","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"cc157733-011b-44e0-9cb7-06af4936fcb0","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_01G8JsiXt7RUxVQeYJuB9eae","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_013dG5YZzdVFwRNZePPpX5b5","name":"Read","input":{"file_path":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/composant-react.php"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":6,"cache_creation_input_tokens":2425,"cache_read_input_tokens":25547,"output_tokens":310,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":2425},"inference_geo":"not_available","iterations":[{"input_tokens":6,"output_tokens":310,"cache_read_input_tokens":25547,"cache_creation_input_tokens":2425,"cache_creation":{"ephemeral_5m_input_tokens":2425,"ephemeral_1h_input_tokens":0},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011Cc3VbD4DHqmxEfc53ZEcV","attributionAgent":"Explore","type":"assistant","uuid":"af0b3b49-a22c-4e0f-8e91-f0a38966c9bb","timestamp":"2026-06-14T16:48:11.347Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"af0b3b49-a22c-4e0f-8e91-f0a38966c9bb","isSidechain":true,"promptId":"7f3e3cb7-906e-4e3a-92fd-290df6bb8908","agentId":"a80d1ba9a1945c3c3","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_013dG5YZzdVFwRNZePPpX5b5","type":"tool_result","content":"1\t<?php\n2\t// Layout composant_react — escape hatch : monte un composant interactif codé côté front. Pas d'entête.\n3\t\n4\tdefined('ABSPATH') || exit;\n5\t\n6\t$l = 'composant_react';\n7\t\n8\treturn cvb_layout($l, 'Composant React', [\n9\t    cvb_select(\"field_cvb_{$l}_composant\", 'composant', 'Composant', [\n10\t        'simulateur_energie'   => 'Simulateur économie d\\'énergie',\n11\t        'diagnostic_toiture'   => 'Diagnostic toiture',\n12\t        'estimation'           => 'Estimation',\n13\t        'climate_dashboard'    => 'Climate dashboard',\n14\t        'applicateur_screener' => 'Screener applicateur',\n15\t        'formulaire_hubspot'   => 'Formulaire HubSpot',\n16\t        'carte_intervention'   => 'Carte d\\'intervention',\n17\t    ], null),\n18\t    cvb_field(\"field_cvb_{$l}_titre\", 'titre', 'Titre', 'text'),\n19\t    cvb_field(\"field_cvb_{$l}_hubspot_form_id\", 'hubspot_form_id', 'ID formulaire HubSpot', 'text'),\n20\t]);\n21\t"}]},"uuid":"3557c9b5-e14d-4c44-9eb2-fc53d5629517","timestamp":"2026-06-14T16:48:11.365Z","sourceToolAssistantUUID":"af0b3b49-a22c-4e0f-8e91-f0a38966c9bb","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"3557c9b5-e14d-4c44-9eb2-fc53d5629517","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_01QkVLp3ks4xA9wcssdM8uUC","type":"message","role":"assistant","content":[{"type":"text","text":"Now let me check the ACF field group definition for the \"origine\" field:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":6,"cache_creation_input_tokens":3703,"cache_read_input_tokens":27972,"cache_creation":{"ephemeral_5m_input_tokens":3703,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"diagnostics":null},"requestId":"req_011Cc3VbQiwKSfpZEQ6mtFQN","attributionAgent":"Explore","type":"assistant","uuid":"96bfd865-e998-48f3-aee3-fd0ab17615e3","timestamp":"2026-06-14T16:48:13.543Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"96bfd865-e998-48f3-aee3-fd0ab17615e3","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_01QkVLp3ks4xA9wcssdM8uUC","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01QXytpMhN8qaYGtxAe5eCGR","name":"Bash","input":{"command":"find /opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts -type f -name \"*.php\" | head -20"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":6,"cache_creation_input_tokens":3703,"cache_read_input_tokens":27972,"output_tokens":123,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":3703},"inference_geo":"not_available","iterations":[{"input_tokens":6,"output_tokens":123,"cache_read_input_tokens":27972,"cache_creation_input_tokens":3703,"cache_creation":{"ephemeral_5m_input_tokens":3703,"ephemeral_1h_input_tokens":0},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011Cc3VbQiwKSfpZEQ6mtFQN","attributionAgent":"Explore","type":"assistant","uuid":"13312395-4d16-447a-b198-f17a35e2cffe","timestamp":"2026-06-14T16:48:14.383Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"13312395-4d16-447a-b198-f17a35e2cffe","isSidechain":true,"promptId":"7f3e3cb7-906e-4e3a-92fd-290df6bb8908","agentId":"a80d1ba9a1945c3c3","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01QXytpMhN8qaYGtxAe5eCGR","type":"tool_result","content":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/hero.php\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/tableau-situations.php\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/timeline.php\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/faq.php\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/cta.php\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/citation.php\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/tableau-comparatif.php\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/logos.php\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/certifications.php\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/compatibilite-supports.php\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/barre-reassurance.php\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/references-grille.php\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/video.php\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/texte-image.php\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/contenu-seo.php\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/specs-techniques.php\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/variantes-produit.php\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/chiffres.php\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/grille-cards.php\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/composant-react.php","is_error":false}]},"uuid":"b10a50f4-9b20-4c54-8468-443689890d57","timestamp":"2026-06-14T16:48:14.502Z","sourceToolAssistantUUID":"13312395-4d16-447a-b198-f17a35e2cffe","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"b10a50f4-9b20-4c54-8468-443689890d57","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_01B5rx5izAaj1ZSLCiBksmrh","type":"message","role":"assistant","content":[{"type":"text","text":"Let me check where the \"origine\" field is defined:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":4,"cache_creation_input_tokens":1174,"cache_read_input_tokens":31675,"cache_creation":{"ephemeral_5m_input_tokens":1174,"ephemeral_1h_input_tokens":0},"output_tokens":4,"service_tier":"standard","inference_geo":"not_available"},"diagnostics":null},"requestId":"req_011Cc3VbdkGjMUAWxB4ZMVvn","attributionAgent":"Explore","type":"assistant","uuid":"9da8db61-68c9-4062-ab5b-2c754ead76fb","timestamp":"2026-06-14T16:48:16.079Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"9da8db61-68c9-4062-ab5b-2c754ead76fb","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_01B5rx5izAaj1ZSLCiBksmrh","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01RJw4pEqqcygdSE4xfqbSq4","name":"Bash","input":{"command":"grep -r \"origine\" /opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/*.php | head -30"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":4,"cache_creation_input_tokens":1174,"cache_read_input_tokens":31675,"output_tokens":116,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":1174},"inference_geo":"not_available","iterations":[{"input_tokens":4,"output_tokens":116,"cache_read_input_tokens":31675,"cache_creation_input_tokens":1174,"cache_creation":{"ephemeral_5m_input_tokens":1174,"ephemeral_1h_input_tokens":0},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011Cc3VbdkGjMUAWxB4ZMVvn","attributionAgent":"Explore","type":"assistant","uuid":"17c0c6bc-440d-4225-b8c7-d30e27ffb3fd","timestamp":"2026-06-14T16:48:16.318Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"17c0c6bc-440d-4225-b8c7-d30e27ffb3fd","isSidechain":true,"promptId":"7f3e3cb7-906e-4e3a-92fd-290df6bb8908","agentId":"a80d1ba9a1945c3c3","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01RJw4pEqqcygdSE4xfqbSq4","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"uuid":"d4b0c8f4-2429-4cb5-bc50-a4d2c40295e6","timestamp":"2026-06-14T16:48:16.385Z","sourceToolAssistantUUID":"17c0c6bc-440d-4225-b8c7-d30e27ffb3fd","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"d4b0c8f4-2429-4cb5-bc50-a4d2c40295e6","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_01Lc5agpeRuRaNRu5yh2ddCZ","type":"message","role":"assistant","content":[{"type":"text","text":"Let me search for the \"origine\" field definition in the field-groups directory:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":135,"cache_read_input_tokens":32849,"cache_creation":{"ephemeral_5m_input_tokens":135,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"diagnostics":null},"requestId":"req_011Cc3VbnDGFEUSHgN5J8wQf","attributionAgent":"Explore","type":"assistant","uuid":"590b2e75-9623-445b-9c50-f72a4ab25e08","timestamp":"2026-06-14T16:48:17.906Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"590b2e75-9623-445b-9c50-f72a4ab25e08","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_01Lc5agpeRuRaNRu5yh2ddCZ","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_017iURXd1aTF565g25YDh1Kd","name":"Bash","input":{"command":"find /opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups -type f -name \"*.php\" | xargs grep -l \"origine\""},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":135,"cache_read_input_tokens":32849,"output_tokens":126,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":135},"inference_geo":"not_available","iterations":[{"input_tokens":3,"output_tokens":126,"cache_read_input_tokens":32849,"cache_creation_input_tokens":135,"cache_creation":{"ephemeral_5m_input_tokens":135,"ephemeral_1h_input_tokens":0},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011Cc3VbnDGFEUSHgN5J8wQf","attributionAgent":"Explore","type":"assistant","uuid":"1f6110fe-738f-4f8b-b23f-1fcc518f969c","timestamp":"2026-06-14T16:48:18.009Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"1f6110fe-738f-4f8b-b23f-1fcc518f969c","isSidechain":true,"promptId":"7f3e3cb7-906e-4e3a-92fd-290df6bb8908","agentId":"a80d1ba9a1945c3c3","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_017iURXd1aTF565g25YDh1Kd","type":"tool_result","content":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/helpers.php","is_error":false}]},"uuid":"a03b92e7-ceda-4df5-81c6-428a65fab808","timestamp":"2026-06-14T16:48:18.078Z","sourceToolAssistantUUID":"1f6110fe-738f-4f8b-b23f-1fcc518f969c","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"a03b92e7-ceda-4df5-81c6-428a65fab808","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_01HMn1uu67gqoAYw6Ev7W3NQ","type":"message","role":"assistant","content":[{"type":"text","text":"Let me read the helpers file:"}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":179,"cache_read_input_tokens":32984,"output_tokens":102,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":179},"inference_geo":"not_available","iterations":[{"input_tokens":3,"output_tokens":102,"cache_read_input_tokens":32984,"cache_creation_input_tokens":179,"cache_creation":{"ephemeral_5m_input_tokens":179,"ephemeral_1h_input_tokens":0},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011Cc3Vbu2XzM1ig5NaEHvpT","attributionAgent":"Explore","type":"assistant","uuid":"ba4f7dc9-2cdf-4fc7-9c0b-7fb0fee78189","timestamp":"2026-06-14T16:48:19.647Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"ba4f7dc9-2cdf-4fc7-9c0b-7fb0fee78189","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_01HMn1uu67gqoAYw6Ev7W3NQ","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01GMRFpmVVGBLHfBm3R2z9ZG","name":"Read","input":{"file_path":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/helpers.php"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":179,"cache_read_input_tokens":32984,"output_tokens":102,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":179},"inference_geo":"not_available","iterations":[{"input_tokens":3,"output_tokens":102,"cache_read_input_tokens":32984,"cache_creation_input_tokens":179,"cache_creation":{"ephemeral_5m_input_tokens":179,"ephemeral_1h_input_tokens":0},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011Cc3Vbu2XzM1ig5NaEHvpT","attributionAgent":"Explore","type":"assistant","uuid":"5bcf2a63-e0e3-4ffb-bd60-4ee368e0a305","timestamp":"2026-06-14T16:48:19.669Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"5bcf2a63-e0e3-4ffb-bd60-4ee368e0a305","isSidechain":true,"promptId":"7f3e3cb7-906e-4e3a-92fd-290df6bb8908","agentId":"a80d1ba9a1945c3c3","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01GMRFpmVVGBLHfBm3R2z9ZG","type":"tool_result","content":"1\t<?php\n2\t/**\n3\t * Helpers builder ACF — préfixe cvb_.\n4\t *\n5\t * Les « clones » du SCHEMA (clone_entete_section, clone_cta) sont implémentés\n6\t * en champs de type group + sous-champs partagés via helpers : le schéma\n7\t * GraphQL produit est exactement entete { badge titre intro } et\n8\t * ctaPrimaire { label lien style }, ce que les clones ACF ne garantissent pas.\n9\t */\n10\t\n11\tdefined('ABSPATH') || exit;\n12\t\n13\t/** snake_case → camelCase (convention graphql_field_name du SCHEMA). */\n14\tfunction cvb_camel(string $snake): string {\n15\t    $parts = explode('_', $snake);\n16\t    return array_shift($parts) . implode('', array_map('ucfirst', $parts));\n17\t}\n18\t\n19\t/** Champ ACF générique : show_in_graphql + graphql_field_name systématiques. */\n20\tfunction cvb_field(string $key, string $name, string $label, string $type, array $extra = []): array {\n21\t    return array_merge([\n22\t        'key'                => $key,\n23\t        'name'               => $name,\n24\t        'label'              => $label,\n25\t        'type'               => $type,\n26\t        'show_in_graphql'    => 1,\n27\t        'graphql_field_name' => cvb_camel($name),\n28\t    ], $extra);\n29\t}\n30\t\n31\t/** Champ image : return_format array (cf. conventions SCHEMA). */\n32\tfunction cvb_image(string $key, string $name, string $label, array $extra = []): array {\n33\t    return cvb_field($key, $name, $label, 'image', array_merge([\n34\t        'return_format' => 'array',\n35\t        'preview_size'  => 'medium',\n36\t    ], $extra));\n37\t}\n38\t\n39\t/** Repeater : collapsed automatique sur le sous-champ « titre » s'il existe. */\n40\tfunction cvb_repeater(string $key, string $name, string $label, array $sub_fields, array $extra = []): array {\n41\t    if (!isset($extra['collapsed'])) {\n42\t        foreach ($sub_fields as $sub) {\n43\t            if (($sub['name'] ?? '') === 'titre') {\n44\t                $extra['collapsed'] = $sub['key'];\n45\t                break;\n46\t            }\n47\t        }\n48\t    }\n49\t\n50\t    return cvb_field($key, $name, $label, 'repeater', array_merge([\n51\t        'layout'       => 'block',\n52\t        'button_label' => 'Ajouter une ligne',\n53\t        'sub_fields'   => $sub_fields,\n54\t    ], $extra));\n55\t}\n56\t\n57\t/** Select simple. */\n58\tfunction cvb_select(string $key, string $name, string $label, array $choices, ?string $default = null, array $extra = []): array {\n59\t    return cvb_field($key, $name, $label, 'select', array_merge([\n60\t        'choices'       => $choices,\n61\t        'default_value' => $default,\n62\t        'allow_null'    => $default === null ? 1 : 0,\n63\t        'return_format' => 'value',\n64\t    ], $extra));\n65\t}\n66\t\n67\t/** Select icône — liste blanche lucide du SCHEMA. */\n68\tfunction cvb_select_icone(string $key, string $name = 'icone', string $label = 'Icône'): array {\n69\t    $icones = [\n70\t        'thermometer-sun', 'thermometer-snowflake', 'droplets', 'shield-check',\n71\t        'clock', 'euro', 'leaf', 'factory', 'warehouse', 'store', 'building-2',\n72\t        'school', 'tractor', 'sun', 'snowflake', 'wrench', 'hard-hat',\n73\t        'check-circle-2', 'trending-down', 'trending-up', 'zap', 'wind',\n74\t        'cloud-sun', 'gauge', 'ruler', 'paint-roller', 'spray-can', 'map-pin',\n75\t        'phone', 'file-text', 'award', 'users',\n76\t    ];\n77\t\n78\t    return cvb_select($key, $name, $label, array_combine($icones, $icones), null, ['ui' => 1]);\n79\t}\n80\t\n81\t/** Sous-champs du clone_entete_section (badge / titre / intro). */\n82\tfunction cvb_entete_subfields(string $layout): array {\n83\t    $p = \"field_cvb_{$layout}_entete\";\n84\t\n85\t    return [\n86\t        cvb_field(\"{$p}_badge\", 'badge', 'Badge', 'text'),\n87\t        cvb_field(\"{$p}_titre\", 'titre', 'Titre', 'text', [\n88\t            'instructions' => \"Utiliser *texte* pour le span d'accent.\",\n89\t        ]),\n90\t        cvb_field(\"{$p}_intro\", 'intro', 'Intro', 'textarea', ['rows' => 3]),\n91\t    ];\n92\t}\n93\t\n94\t/** Clone entête de section → group `entete` (graphql entete { badge titre intro }). */\n95\tfunction cvb_clone_entete(string $layout): array {\n96\t    return cvb_field(\"field_cvb_{$layout}_entete\", 'entete', 'En-tête de section', 'group', [\n97\t        'layout'     => 'block',\n98\t        'sub_fields' => cvb_entete_subfields($layout),\n99\t    ]);\n100\t}\n101\t\n102\t/** Sous-champs du clone_cta (label / lien / style). */\n103\tfunction cvb_cta_subfields(string $layout, string $name): array {\n104\t    $p = \"field_cvb_{$layout}_{$name}\";\n105\t\n106\t    return [\n107\t        cvb_field(\"{$p}_label\", 'label', 'Label', 'text'),\n108\t        cvb_field(\"{$p}_lien\", 'lien', 'Lien', 'text', [\n109\t            'instructions' => 'URL complète ou chemin interne (/contact).',\n110\t        ]),\n111\t        cvb_select(\"{$p}_style\", 'style', 'Style', [\n112\t            'primary'   => 'Primary',\n113\t            'secondary' => 'Secondary',\n114\t            'ghost'     => 'Ghost',\n115\t        ], 'primary'),\n116\t    ];\n117\t}\n118\t\n119\t/** Clone CTA → group (graphql camelCase du name : cta, ctaPrimaire, ctaNavbar…). */\n120\tfunction cvb_clone_cta(string $layout, string $name = 'cta', string $label = 'CTA'): array {\n121\t    return cvb_field(\"field_cvb_{$layout}_{$name}\", $name, $label, 'group', [\n122\t        'layout'     => 'block',\n123\t        'sub_fields' => cvb_cta_subfields($layout, $name),\n124\t    ]);\n125\t}\n126\t\n127\t/** Enveloppe d'un layout du flexible content `sections`. */\n128\tfunction cvb_layout(string $name, string $label, array $sub_fields): array {\n129\t    // Champ commun `origine` : nom du composant React d'ORIGINE qui rend cette\n130\t    // section (parité pixel avec la version d'avant-CMS). Posé par le seed,\n131\t    // lecture seule côté BO. Vide => rendu générique du layout.\n132\t    $sub_fields[] = [\n133\t        'key'                => \"field_cvb_{$name}_origine\",\n134\t        'name'               => 'origine',\n135\t        'label'              => \"Composant d'origine\",\n136\t        'type'               => 'text',\n137\t        'readonly'           => 1,\n138\t        'instructions'       => 'Géré par les développeurs — composant du site qui rend ce block.',\n139\t        'show_in_graphql'    => 1,\n140\t        'graphql_field_name' => 'origine',\n141\t        'wrapper'            => ['width' => '40'],\n142\t    ];\n143\t    return [\n144\t        'key'        => \"layout_cvb_{$name}\",\n145\t        'name'       => $name,\n146\t        'label'      => $label,\n147\t        'display'    => 'block',\n148\t        'sub_fields' => $sub_fields,\n149\t    ];\n150\t}\n151\t\n152\t/** Onglet ACF (UI seulement, hors GraphQL). */\n153\tfunction cvb_tab(string $key, string $label): array {\n154\t    return [\n155\t        'key'             => $key,\n156\t        'label'           => $label,\n157\t        'name'            => '',\n158\t        'type'            => 'tab',\n159\t        'placement'       => 'top',\n160\t        'show_in_graphql' => 0,\n161\t    ];\n162\t}\n163\t"}]},"uuid":"16c00b48-013e-4e22-a17d-620d3c1eba5e","timestamp":"2026-06-14T16:48:19.688Z","sourceToolAssistantUUID":"5bcf2a63-e0e3-4ffb-bd60-4ee368e0a305","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"16c00b48-013e-4e22-a17d-620d3c1eba5e","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_01RNpsSBFMLUB8F2Cwn2noZr","type":"message","role":"assistant","content":[{"type":"text","text":"Perfect! Now let me look at the origine files to understand the registry structure:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":2597,"cache_read_input_tokens":33163,"cache_creation":{"ephemeral_5m_input_tokens":2597,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"diagnostics":null},"requestId":"req_011Cc3Vc2MpARzQHdsMJWyym","attributionAgent":"Explore","type":"assistant","uuid":"a01cf7a2-8533-4091-abd2-e3284fc05753","timestamp":"2026-06-14T16:48:20.942Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"a01cf7a2-8533-4091-abd2-e3284fc05753","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_01RNpsSBFMLUB8F2Cwn2noZr","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01EbJDXfCjabNmS9yE5CzhW6","name":"Read","input":{"file_path":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/origine/home.tsx"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":2597,"cache_read_input_tokens":33163,"output_tokens":165,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":2597},"inference_geo":"not_available","iterations":[{"input_tokens":3,"output_tokens":165,"cache_read_input_tokens":33163,"cache_creation_input_tokens":2597,"cache_creation":{"ephemeral_5m_input_tokens":2597,"ephemeral_1h_input_tokens":0},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011Cc3Vc2MpARzQHdsMJWyym","attributionAgent":"Explore","type":"assistant","uuid":"a05e6058-6f51-4bca-b74b-ff46545dacdd","timestamp":"2026-06-14T16:48:21.801Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"a05e6058-6f51-4bca-b74b-ff46545dacdd","isSidechain":true,"promptId":"7f3e3cb7-906e-4e3a-92fd-290df6bb8908","agentId":"a80d1ba9a1945c3c3","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01EbJDXfCjabNmS9yE5CzhW6","type":"tool_result","content":"1\t// Adaptateurs origine — famille \"home\". Une entrée par section de la page\n2\t// d'accueil, keyée par le NOM DE FICHIER du composant original. Chaque\n3\t// adaptateur reçoit la section WordPress (champs camelCase GraphQL, cf.\n4\t// src/lib/wp/types.ts) et rend le composant ORIGINAL en mappant les champs.\n5\t// Valeur WP absente/null => prop undefined (le défaut du composant prend le\n6\t// relais, rendu identique au site pré-CMS).\n7\timport { Fragment, type ComponentType, type ReactNode } from \"react\";\n8\timport { arr, mapImage, parseAccent } from \"@/lib/wp/mappers\";\n9\timport type {\n10\t  ChiffresBlock,\n11\t  CtaBlock,\n12\t  ContenuSeoBlock,\n13\t  EtapesBlock,\n14\t  FaqBlock,\n15\t  GrilleCardsBlock,\n16\t  GrilleSecteursBlock,\n17\t  HeroBlock,\n18\t  LogosBlock,\n19\t  TableauComparatifBlock,\n20\t  TemoignagesVideoBlock,\n21\t  TexteImageBlock,\n22\t} from \"@/lib/wp/types\";\n23\t\n24\timport Hero from \"@/components/Hero\";\n25\timport ConstatSection from \"@/components/ConstatSection\";\n26\timport CoolRoofExplainerSection from \"@/components/CoolRoofExplainerSection\";\n27\timport WinterObjectionSection from \"@/components/WinterObjectionSection\";\n28\timport SectorsGrid from \"@/components/SectorsGrid\";\n29\timport SystemSection, { defaultLayers } from \"@/components/SystemSection\";\n30\timport PriceComparisonSection from \"@/components/PriceComparisonSection\";\n31\timport SocialProofSection from \"@/components/SocialProofSection\";\n32\timport PriceVSSection from \"@/components/PriceVSSection\";\n33\timport RSESection, { defaultItems as defaultRseItems } from \"@/components/RSESection\";\n34\timport Testimonials from \"@/components/Testimonials\";\n35\timport ProcessSection from \"@/components/ProcessSection\";\n36\timport ExpertiseSection, { defaultCards } from \"@/components/ExpertiseSection\";\n37\timport FAQSection from \"@/components/FAQ\";\n38\timport PressSection from \"@/components/PressSection\";\n39\timport CTASection from \"@/components/CTASection\";\n40\timport BlogPreview from \"@/components/BlogPreview\";\n41\timport SEOContent from \"@/components/SEOContent\";\n42\timport ApplicatorsSection from \"@/components/ApplicatorsSection\";\n43\t\n44\t// ---------------------------------------------------------------------------\n45\t// Helpers de mapping\n46\t// ---------------------------------------------------------------------------\n47\t\n48\t/** Décode les entités HTML courantes des champs WP. */\n49\tconst decode = (s: string): string =>\n50\t  s\n51\t    .replace(/&nbsp;/g, \" \")\n52\t    .replace(/&amp;/g, \"&\")\n53\t    .replace(/&lt;/g, \"<\")\n54\t    .replace(/&gt;/g, \">\")\n55\t    .replace(/&quot;/g, '\"')\n56\t    .replace(/&#0?39;/g, \"'\")\n57\t    .replace(/&(?:rsquo|lsquo|#8216|#8217);/g, \"'\")\n58\t    .replace(/&(?:rdquo|#8221);/g, \"”\")\n59\t    .replace(/&(?:ldquo|#8220);/g, \"“\")\n60\t    .replace(/&(?:ndash|#8211);/g, \"–\")\n61\t    .replace(/&(?:mdash|#8212);/g, \"—\")\n62\t    .replace(/&(?:hellip|#8230);/g, \"…\");\n63\t\n64\t/** Texte brut d'un fragment HTML (balises supprimées). */\n65\tconst text = (html?: string | null): string | undefined => {\n66\t  if (!html) return undefined;\n67\t  const t = decode(html.replace(/<[^>]+>/g, \"\")).trim();\n68\t  return t || undefined;\n69\t};\n70\t\n71\t/** Titre plain-text : convention *accent* retirée (composants sans span). */\n72\tconst plain = (titre?: string | null): string | undefined =>\n73\t  titre ? titre.replace(/\\*/g, \"\") : undefined;\n74\t\n75\t/**\n76\t * Reconstruit le markup de titre de l'original à partir de la convention\n77\t * `*accent*` : les segments accent sont rendus dans un <span> portant la\n78\t * classe EXACTE de l'original ; `br` insère le <br /> de l'original avant\n79\t * le span (titres sur deux lignes).\n80\t */\n81\tconst accentTitle = (\n82\t  titre: string | null | undefined,\n83\t  spanClass: string,\n84\t  opts?: { br?: boolean },\n85\t): ReactNode | undefined => {\n86\t  if (!titre || !titre.includes(\"*\")) return plain(titre);\n87\t  const segments = parseAccent(titre);\n88\t  return (\n89\t    <>\n90\t      {segments.map((s, i) =>\n91\t        s.accent ? (\n92\t          <Fragment key={i}>\n93\t            {opts?.br && i > 0 && <br />}\n94\t            <span className={spanClass}>{s.text}</span>\n95\t          </Fragment>\n96\t        ) : (\n97\t          <Fragment key={i}>{opts?.br ? s.text.replace(/\\s+$/, \"\") : s.text}</Fragment>\n98\t        ),\n99\t      )}\n100\t    </>\n101\t  );\n102\t};\n103\t\n104\t/** Contenus (innerHTML) des blocs <p> d'un wysiwyg. */\n105\tconst pBlocks = (html?: string | null): string[] =>\n106\t  html ? [...html.matchAll(/<p[^>]*>([\\s\\S]*?)<\\/p>/g)].map((m) => m[1]) : [];\n107\t\n108\t/** Paires <h3>titre</h3><p>texte</p> d'un wysiwyg. */\n109\tconst h3pPairs = (html?: string | null): { title: string; desc: string }[] =>\n110\t  html\n111\t    ? [...html.matchAll(/<h3[^>]*>([\\s\\S]*?)<\\/h3>\\s*<p[^>]*>([\\s\\S]*?)<\\/p>/g)].map((m) => ({\n112\t        title: text(m[1]) ?? \"\",\n113\t        desc: text(m[2]) ?? \"\",\n114\t      }))\n115\t    : [];\n116\t\n117\t/** Items <p><strong>titre</strong><br />texte</p> d'un wysiwyg. */\n118\tconst strongBrItems = (html?: string | null): { title: string; text: string }[] =>\n119\t  html\n120\t    ? [...html.matchAll(/<p[^>]*>\\s*<strong>([\\s\\S]*?)<\\/strong>\\s*<br\\s*\\/?>\\s*([\\s\\S]*?)<\\/p>/g)].map(\n121\t        (m) => ({ title: text(m[1]) ?? \"\", text: text(m[2]) ?? \"\" }),\n122\t      )\n123\t    : [];\n124\t\n125\t/** Ajoute la classe des <strong> de l'original au HTML WP. */\n126\tconst withStrongClass = (html: string, cls: string): string =>\n127\t  html.replace(/<strong>/g, `<strong class=\"${cls}\">`);\n128\t\n129\t/**\n130\t * Annule wptexturize sur un fragment HTML injecté tel quel : ramène les\n131\t * guillemets/apostrophes typographiques aux caractères droits des littéraux\n132\t * JSX d'origine (parité pixel avec le site pré-CMS).\n133\t */\n134\tconst detexturize = (html: string): string =>\n135\t  html\n136\t    .replace(/[‘’]|&(?:rsquo|lsquo|#8216|#8217);/g, \"'\")\n137\t    .replace(/[“”]|&(?:ldquo|rdquo|#8220|#8221);/g, '\"');\n138\t\n139\t/** Id YouTube depuis une URL watch / youtu.be / embed. */\n140\tconst youtubeId = (url?: string | null): string | undefined => {\n141\t  if (!url) return undefined;\n142\t  const m =\n143\t    url.match(/[?&]v=([\\w-]{6,})/) ??\n144\t    url.match(/youtu\\.be\\/([\\w-]{6,})/) ??\n145\t    url.match(/\\/embed\\/([\\w-]{6,})/);\n146\t  return m?.[1];\n147\t};\n148\t\n149\t/** Sépare le texte d'une étape de la citation « … » — auteur du seed. */\n150\tconst splitExpert = (texte?: string | null): { description?: string; expert?: string } => {\n151\t  if (!texte) return {};\n152\t  const m = texte.match(/\\n\\n«\\s*([\\s\\S]*?)\\s*»/);\n153\t  if (!m || m.index === undefined) return { description: texte };\n154\t  return { description: texte.slice(0, m.index).trim(), expert: m[1].trim() };\n155\t};\n156\t\n157\t/** \"Titre — sous-titre\" → [titre, sous-titre]. */\n158\tconst splitDash = (s?: string | null): [string | undefined, string | undefined] => {\n159\t  if (!s) return [undefined, undefined];\n160\t  const idx = s.indexOf(\"—\");\n161\t  if (idx < 0) return [s.trim() || undefined, undefined];\n162\t  return [s.slice(0, idx).trim() || undefined, s.slice(idx + 1).trim() || undefined];\n163\t};\n164\t\n165\t// ---------------------------------------------------------------------------\n166\t// Adaptateurs\n167\t// ---------------------------------------------------------------------------\n168\t\n169\t/** Hero original COMPLET (formulaire d'estimation inclus, codé en dur). */\n170\tconst HeroAdapter = (section: HeroBlock) => {\n171\t  const img = mapImage(section.image);\n172\t  return (\n173\t    <Hero\n174\t      titre={plain(section.titre)}\n175\t      lead={\n176\t        section.lead ? (\n177\t          <span\n178\t            dangerouslySetInnerHTML={{\n179\t              __html: withStrongClass(detexturize(section.lead), \"text-white font-semibold\"),\n180\t            }}\n181\t          />\n182\t        ) : undefined\n183\t      }\n184\t      imageSrc={img?.sourceUrl}\n185\t      imageAlt={img?.altText || undefined}\n186\t    />\n187\t  );\n188\t};\n189\t\n190\tconst ConstatAdapter = (section: TexteImageBlock) => {\n191\t  const img = mapImage(section.image);\n192\t  const pairs = h3pPairs(section.contenu);\n193\t  return (\n194\t    <ConstatSection\n195\t      titre={accentTitle(section.entete?.titre, \"text-foreground/30\")}\n196\t      blocs={pairs.length ? pairs : undefined}\n197\t      imageSrc={img?.sourceUrl}\n198\t      imageAlt={img?.altText || undefined}\n199\t      note={section.note ?? undefined}\n200\t    />\n201\t  );\n202\t};\n203\t\n204\tconst CoolRoofExplainerAdapter = (section: TexteImageBlock) => {\n205\t  const img = mapImage(section.image);\n206\t  const inner = pBlocks(section.contenu)[0];\n207\t  return (\n208\t    <CoolRoofExplainerSection\n209\t      titre={accentTitle(section.entete?.titre, \"text-foreground/30\")}\n210\t      paragraphe={\n211\t        inner ? (\n212\t          <span\n213\t            dangerouslySetInnerHTML={{\n214\t              __html: withStrongClass(detexturize(inner), \"text-foreground/80 font-semibold\"),\n215\t            }}\n216\t          />\n217\t        ) : undefined\n218\t      }\n219\t      imageSrc={img?.sourceUrl}\n220\t      imageAlt={img?.altText || undefined}\n221\t      note={section.note ?? undefined}\n222\t      ctaLabel={section.cta?.label ?? undefined}\n223\t      ctaHref={section.cta?.lien ?? undefined}\n224\t    />\n225\t  );\n226\t};\n227\t\n228\tconst WinterObjectionAdapter = (section: TexteImageBlock) => {\n229\t  const img = mapImage(section.image);\n230\t  const blocks = pBlocks(section.contenu);\n231\t  // 1er <p> (en <strong>) = accroche, les suivants = paragraphes.\n232\t  const intro = blocks.length ? text(blocks[0]) : undefined;\n233\t  return (\n234\t    <WinterObjectionSection\n235\t      titre={accentTitle(section.entete?.titre, \"text-teal-vivid\", { br: true })}\n236\t      intro={intro}\n237\t      paragraphe1={blocks[1] ? text(blocks[1]) : undefined}\n238\t      paragraphe2={blocks[2] ? text(blocks[2]) : undefined}\n239\t      imageSrc={img?.sourceUrl}\n240\t      imageAlt={img?.altText || undefined}\n241\t    />\n242\t  );\n243\t};\n244\t\n245\tconst SectorsGridAdapter = (section: GrilleSecteursBlock) => (\n246\t  <SectorsGrid\n247\t    badge={section.entete?.badge ?? undefined}\n248\t    titre={accentTitle(section.entete?.titre, \"text-foreground/30\", { br: true })}\n249\t    intro={section.entete?.intro ?? undefined}\n250\t  />\n251\t);\n252\t\n253\tconst SystemAdapter = (section: EtapesBlock) => {\n254\t  const etapes = arr(section.etapes);\n255\t  const layers = etapes.length\n256\t    ? defaultLayers.map((layer, i) => {\n257\t        const e = etapes[i];\n258\t        if (!e) return layer;\n259\t        const { description, expert } = splitExpert(e.texte);\n260\t        return {\n261\t          ...layer,\n262\t          title: e.titre ?? layer.title,\n263\t          description: description ?? layer.description,\n264\t          expert: expert ?? layer.expert,\n265\t        };\n266\t      })\n267\t    : undefined;\n268\t  const img = etapes.map((e) => mapImage(e.image)).find(Boolean) ?? null;\n269\t  return (\n270\t    <SystemSection\n271\t      badge={section.entete?.badge ?? undefined}\n272\t      titre={accentTitle(section.entete?.titre, \"text-foreground/30\")}\n273\t      intro={section.entete?.intro ?? undefined}\n274\t      layers={layers}\n275\t      imageSrc={img?.sourceUrl}\n276\t      imageAlt={img?.altText || undefined}\n277\t    />\n278\t  );\n279\t};\n280\t\n281\tconst PriceComparisonAdapter = (section: TableauComparatifBlock) => {\n282\t  const cols = arr(section.entetesColonnes).map((c) => splitDash(c.texte));\n283\t  // cols[0] = colonne Covalba (logo affiché, seul le prix est repris).\n284\t  const prix = cols.length === 4 ? cols.map(([, p]) => p ?? \"\") : undefined;\n285\t  const colonnes = cols.length === 4 ? cols.slice(1).map(([n]) => n ?? \"\") : undefined;\n286\t  const lignes = arr(section.lignesComparatif);\n287\t  const features = lignes.length\n288\t    ? lignes.map((l) => {\n289\t        const c = arr(l.cellules).map((x) => x.texte ?? \"\");\n290\t        return {\n291\t          label: c[0] ?? \"\",\n292\t          covalba: c[1] === \"oui\",\n293\t          clim: c[2] === \"oui\",\n294\t          membrane: c[3] === \"oui\",\n295\t          refection: c[4] === \"oui\",\n296\t        };\n297\t      })\n298\t    : undefined;\n299\t  // texte_apres = \"Titre de l'encart. Corps de l'encart…\"\n300\t  let encartTitre: string | undefined;\n301\t  let encartTexte: string | undefined;\n302\t  if (section.texteApres) {\n303\t    const idx = section.texteApres.indexOf(\". \");\n304\t    if (idx > 0) {\n305\t      encartTitre = section.texteApres.slice(0, idx + 1);\n306\t      encartTexte = section.texteApres.slice(idx + 2);\n307\t    } else {\n308\t      encartTexte = section.texteApres;\n309\t    }\n310\t  }\n311\t  return (\n312\t    <PriceComparisonSection\n313\t      badge={section.entete?.badge ?? undefined}\n314\t      titre={accentTitle(section.entete?.titre, \"text-foreground/30\")}\n315\t      intro={section.entete?.intro ?? undefined}\n316\t      features={features}\n317\t      prix={prix}\n318\t      colonnes={colonnes}\n319\t      encartTitre={encartTitre}\n320\t      encartTexte={encartTexte}\n321\t      ctaLabel={section.cta?.label ?? undefined}\n322\t      ctaHref={section.cta?.lien ?? undefined}\n323\t    />\n324\t  );\n325\t};\n326\t\n327\tconst SocialProofAdapter = (section: ChiffresBlock) => {\n328\t  const figures = arr(section.figures);\n329\t  const rating = figures[0];\n330\t  const stats = figures.slice(1).map((f) => ({\n331\t    value: f.value ?? \"\",\n332\t    label: f.label ?? \"\",\n333\t    sub: f.sublabel ?? \"\",\n334\t  }));\n335\t  return (\n336\t    <SocialProofSection\n337\t      ratingValue={rating?.value ?? undefined}\n338\t      ratingLabel={rating?.label ?? undefined}\n339\t      stats={stats.length ? stats : undefined}\n340\t    />\n341\t  );\n342\t};\n343\t\n344\tconst PriceVSAdapter = (section: TableauComparatifBlock) => {\n345\t  const cols = arr(section.entetesColonnes).map((c) => splitDash(c.texte));\n346\t  const [leftTitle, leftSub] = cols[0] ?? [undefined, undefined];\n347\t  const [rightTitle, rightSub] = cols[1] ?? [undefined, undefined];\n348\t  const lignes = arr(section.lignesComparatif).map((l) => arr(l.cellules).map((x) => x.texte ?? \"\"));\n349\t  // 3 premières lignes = valeurs labellisées, lignes \"-\" = puces.\n350\t  const valued = lignes.filter((c) => c[0] && c[0] !== \"-\");\n351\t  const bullets = lignes.filter((c) => !c[0] || c[0] === \"-\");\n352\t  const stripM2 = (s?: string) => (s ? s.replace(/\\s*\\/\\s*m²\\s*$/, \"\").replace(/\\/m²$/, \"\") : undefined);\n353\t  const find = (label: string) => valued.find((c) => c[0].toLowerCase().includes(label));\n354\t  const prixRow = find(\"prix\");\n355\t  const dureeRow = find(\"durée\");\n356\t  const coutRow = find(\"coût\");\n357\t  const leftPoints = bullets.map((c) => c[1]).filter(Boolean);\n358\t  const rightPoints = bullets.map((c) => c[2]).filter(Boolean);\n359\t  return (\n360\t    <PriceVSSection\n361\t      badge={section.entete?.badge ?? undefined}\n362\t      titre={accentTitle(section.entete?.titre, \"text-foreground\")}\n363\t      intro={section.entete?.intro ?? undefined}\n364\t      labelPrix={prixRow?.[0]}\n365\t      labelDuree={dureeRow?.[0]}\n366\t      labelCout={coutRow?.[0]}\n367\t      leftTitle={leftTitle}\n368\t      leftSub={leftSub}\n369\t      leftPrix={prixRow?.[1]}\n370\t      leftDuree={dureeRow?.[1]}\n371\t      leftCout={stripM2(coutRow?.[1])}\n372\t      leftPoints={leftPoints.length ? leftPoints : undefined}\n373\t      rightTitle={rightTitle}\n374\t      rightSub={rightSub}\n375\t      rightPrix={prixRow?.[2]}\n376\t      rightDuree={dureeRow?.[2]}\n377\t      rightCout={stripM2(coutRow?.[2])}\n378\t      rightPoints={rightPoints.length ? rightPoints : undefined}\n379\t      conclusion={section.texteApres ?? undefined}\n380\t      ctaLabel={section.cta?.label ?? undefined}\n381\t      ctaHref={section.cta?.lien ?? undefined}\n382\t    />\n383\t  );\n384\t};\n385\t\n386\tconst RSEAdapter = (section: TexteImageBlock) => {\n387\t  const img = mapImage(section.image);\n388\t  const parsed = strongBrItems(section.contenu);\n389\t  const items = parsed.length\n390\t    ? defaultRseItems.map((item, i) => {\n391\t        const p = parsed[i];\n392\t        if (!p) return item;\n393\t        return { ...item, title: p.title || item.title, text: p.text || item.text };\n394\t      })\n395\t    : undefined;\n396\t  return (\n397\t    <RSESection\n398\t      badge={section.entete?.badge ?? undefined}\n399\t      titre={accentTitle(section.entete?.titre, \"text-foreground/30\")}\n400\t      items={items}\n401\t      imageSrc={img?.sourceUrl}\n402\t      imageAlt={img?.altText || undefined}\n403\t    />\n404\t  );\n405\t};\n406\t\n407\tconst TestimonialsAdapter = (section: TemoignagesVideoBlock) => {\n408\t  const temoignages = arr(section.temoignages).map((t) => ({\n409\t    videoId: youtubeId(t.videoUrl) ?? \"\",\n410\t    quote: t.citation ?? \"\",\n411\t    name: t.nom ?? \"\",\n412\t    role: t.role ?? \"\",\n413\t    company: t.entreprise ?? \"\",\n414\t    context: t.contexte ?? \"\",\n415\t  }));\n416\t  return (\n417\t    <Testimonials\n418\t      badge={section.entete?.badge ?? undefined}\n419\t      titre={accentTitle(section.entete?.titre, \"text-teal-vivid\")}\n420\t      intro={section.entete?.intro ?? undefined}\n421\t      testimonials={temoignages.length ? temoignages : undefined}\n422\t    />\n423\t  );\n424\t};\n425\t\n426\tconst ProcessAdapter = (section: EtapesBlock) => {\n427\t  const etapes = arr(section.etapes);\n428\t  // Icônes/numéros : skeleton de l'original conservé côté composant\n429\t  // (ProcessSection est un Client Component, on ne lui passe que des données).\n430\t  const stepsContent = etapes.length\n431\t    ? etapes.map((e) => ({ title: e.titre ?? undefined, text: e.texte ?? undefined }))\n432\t    : undefined;\n433\t  const id = youtubeId(section.videoUrl);\n434\t  return (\n435\t    <ProcessSection\n436\t      badge={section.entete?.badge ?? undefined}\n437\t      titre={accentTitle(section.entete?.titre, \"text-foreground/30\")}\n438\t      intro={section.entete?.intro ?? undefined}\n439\t      stepsContent={stepsContent}\n440\t      reassurance={section.reassurance ?? undefined}\n441\t      videoEmbedUrl={id ? `https://www.youtube.com/embed/${id}?autoplay=1` : undefined}\n442\t    />\n443\t  );\n444\t};\n445\t\n446\tconst ExpertiseAdapter = (section: GrilleCardsBlock) => {\n447\t  const wpCards = arr(section.cards);\n448\t  // Icônes + carte mise en avant : skeleton de l'original conservé,\n449\t  // seuls les textes viennent de WP (champ `icone` WP ignoré).\n450\t  const cards = wpCards.length\n451\t    ? defaultCards.map((card, i) => {\n452\t        const c = wpCards[i];\n453\t        if (!c) return card;\n454\t        return { ...card, title: c.titre ?? card.title, text: c.texte ?? card.text };\n455\t      })\n456\t    : undefined;\n457\t  return (\n458\t    <ExpertiseSection\n459\t      badge={section.entete?.badge ?? undefined}\n460\t      titre={accentTitle(section.entete?.titre, \"text-foreground/30\")}\n461\t      intro={section.entete?.intro ?? undefined}\n462\t      cards={cards}\n463\t    />\n464\t  );\n465\t};\n466\t\n467\tconst FAQAdapter = (section: FaqBlock) => {\n468\t  const faqs = arr(section.questions).map((q) => ({\n469\t    q: q.question ?? \"\",\n470\t    a: q.reponse ?? \"\",\n471\t  }));\n472\t  return (\n473\t    <FAQSection\n474\t      badge={section.entete?.badge ?? undefined}\n475\t      titre={plain(section.entete?.titre)}\n476\t      intro={section.entete?.intro ?? undefined}\n477\t      faqs={faqs.length ? faqs : undefined}\n478\t      ctaLabel={section.cta?.label ?? undefined}\n479\t      ctaHref={section.cta?.lien ?? undefined}\n480\t    />\n481\t  );\n482\t};\n483\t\n484\tconst PressAdapter = (section: LogosBlock) => {\n485\t  const outlets = arr(section.logosCustom)\n486\t    .map((l) => {\n487\t      const img = mapImage(l.image);\n488\t      return {\n489\t        name: l.nom ?? img?.altText ?? \"\",\n490\t        logo: img?.sourceUrl ?? \"\",\n491\t        url: l.lien ?? undefined,\n492\t      };\n493\t    })\n494\t    .filter((o) => o.logo);\n495\t  return (\n496\t    <PressSection\n497\t      label={section.entete?.badge ?? undefined}\n498\t      outlets={outlets.length ? outlets : undefined}\n499\t    />\n500\t  );\n501\t};\n502\t\n503\tconst CTAAdapter = (section: CtaBlock) => (\n504\t  <CTASection\n505\t    title={plain(section.titre)}\n506\t    subtitle={section.texte ?? undefined}\n507\t    primaryLabel={section.ctaPrimaire?.label ?? undefined}\n508\t    primaryHref={section.ctaPrimaire?.lien ?? undefined}\n509\t    secondaryLabel={section.ctaSecondaire?.label ?? undefined}\n510\t    secondaryHref={section.ctaSecondaire?.lien ?? undefined}\n511\t  />\n512\t);\n513\t\n514\tconst BlogPreviewAdapter = (section: GrilleCardsBlock) => {\n515\t  const articles = arr(section.cards)\n516\t    .map((c) => {\n517\t      const img = mapImage(c.image);\n518\t      return {\n519\t        title: c.titre ?? \"\",\n520\t        category: c.texte ?? \"\",\n521\t        image: img?.sourceUrl ?? \"\",\n522\t        href: c.lien ?? undefined,\n523\t      };\n524\t    })\n525\t    .filter((a) => a.title);\n526\t  return (\n527\t    <BlogPreview\n528\t      badge={section.entete?.badge ?? undefined}\n529\t      titre={plain(section.entete?.titre)}\n530\t      articles={articles.length ? articles : undefined}\n531\t    />\n532\t  );\n533\t};\n534\t\n535\tconst SEOContentAdapter = (section: ContenuSeoBlock) => {\n536\t  const secs = arr(section.sections);\n537\t  const sectionsNode = secs.length ? (\n538\t    <>\n539\t      {secs.map((s, i) => (\n540\t        <div key={i}>\n541\t          <h3 className=\"font-satoshi text-2xl lg:text-3xl font-bold text-foreground mb-4\">\n542\t            {s.titre ? decode(s.titre) : s.titre}\n543\t          </h3>\n544\t          <div\n545\t            className=\"space-y-4 [&_strong]:text-foreground [&_a]:text-primary [&_a]:font-semibold hover:[&_a]:underline [&_ul]:list-disc [&_ul]:pl-6 [&_ul]:space-y-3 [&_ol]:list-decimal [&_ol]:pl-6 [&_ol]:space-y-3 [&_table]:w-full [&_table]:text-sm [&_table]:border-collapse [&_table]:!mt-6 [&_th]:text-left [&_th]:font-satoshi [&_th]:font-bold [&_th]:px-4 [&_th]:py-3 [&_thead_th:first-child]:rounded-tl-xl [&_thead_th:last-child]:rounded-tr-xl [&_thead_tr]:bg-foreground [&_thead_tr]:text-white [&_tbody_tr:nth-child(odd)]:bg-white [&_tbody_tr:nth-child(even)]:bg-foreground/[0.03] [&_td]:px-4 [&_td]:py-3 [&_td]:border-b [&_td]:border-foreground/5 [&_td:first-child]:font-semibold [&_td:first-child]:text-foreground [&_td:nth-child(2)]:text-primary [&_td:nth-child(2)]:font-semibold [&_td:nth-child(n+3)]:text-foreground/70\"\n546\t            dangerouslySetInnerHTML={{ __html: detexturize(s.contenu ?? \"\") }}\n547\t          />\n548\t        </div>\n549\t      ))}\n550\t    </>\n551\t  ) : undefined;\n552\t  const sources = arr(section.sources).map((s) => ({\n553\t    ref: s.label ?? \"\",\n554\t    url: s.url || undefined,\n555\t  }));\n556\t  return (\n557\t    <SEOContent\n558\t      intro={text(section.intro)}\n559\t      sections={sectionsNode}\n560\t      sources={sources.length ? sources : undefined}\n561\t    />\n562\t  );\n563\t};\n564\t\n565\tconst ApplicatorsAdapter = (section: TexteImageBlock) => {\n566\t  const img = mapImage(section.image);\n567\t  const benefits = arr(section.liste)\n568\t    .map((l) => l.texte ?? \"\")\n569\t    .filter(Boolean);\n570\t  return (\n571\t    <ApplicatorsSection\n572\t      badge={section.entete?.badge ?? undefined}\n573\t      title={plain(section.entete?.titre)}\n574\t      description={text(section.contenu)}\n575\t      benefits={benefits.length ? benefits : undefined}\n576\t      imageSrc={img?.sourceUrl}\n577\t      imageAlt={img?.altText || undefined}\n578\t      ctaLabel={section.cta?.label ?? undefined}\n579\t      ctaHref={section.cta?.lien ?? undefined}\n580\t    />\n581\t  );\n582\t};\n583\t\n584\t// ---------------------------------------------------------------------------\n585\t// Registry — clé = nom de fichier du composant original.\n586\t// ---------------------------------------------------------------------------\n587\t\n588\texport const origineHome: Record<string, ComponentType<any>> = {\n589\t  Hero: HeroAdapter,\n590\t  ConstatSection: ConstatAdapter,\n591\t  CoolRoofExplainerSection: CoolRoofExplainerAdapter,\n592\t  WinterObjectionSection: WinterObjectionAdapter,\n593\t  SectorsGrid: SectorsGridAdapter,\n594\t  SystemSection: SystemAdapter,\n595\t  PriceComparisonSection: PriceComparisonAdapter,\n596\t  SocialProofSection: SocialProofAdapter,\n597\t  PriceVSSection: PriceVSAdapter,\n598\t  RSESection: RSEAdapter,\n599\t  Testimonials: TestimonialsAdapter,\n600\t  ProcessSection: ProcessAdapter,\n601\t  ExpertiseSection: ExpertiseAdapter,\n602\t  FAQ: FAQAdapter,\n603\t  PressSection: PressAdapter,\n604\t  CTASection: CTAAdapter,\n605\t  BlogPreview: BlogPreviewAdapter,\n606\t  SEOContent: SEOContentAdapter,\n607\t  ApplicatorsSection: ApplicatorsAdapter,\n608\t};\n609\t"}]},"uuid":"46b42381-747d-4ce5-b7ee-666a80f562f2","timestamp":"2026-06-14T16:48:21.811Z","sourceToolAssistantUUID":"a05e6058-6f51-4bca-b74b-ff46545dacdd","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"46b42381-747d-4ce5-b7ee-666a80f562f2","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_01RNpsSBFMLUB8F2Cwn2noZr","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01SGcx7QeEHWB3HVoaH7zJK7","name":"Read","input":{"file_path":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/origine/produits.tsx"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":2597,"cache_read_input_tokens":33163,"output_tokens":165,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":2597},"inference_geo":"not_available","iterations":[{"input_tokens":3,"output_tokens":165,"cache_read_input_tokens":33163,"cache_creation_input_tokens":2597,"cache_creation":{"ephemeral_5m_input_tokens":2597,"ephemeral_1h_input_tokens":0},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011Cc3Vc2MpARzQHdsMJWyym","attributionAgent":"Explore","type":"assistant","uuid":"4d71b52a-2613-41aa-a3cb-91d148216a74","timestamp":"2026-06-14T16:48:21.863Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"4d71b52a-2613-41aa-a3cb-91d148216a74","isSidechain":true,"promptId":"7f3e3cb7-906e-4e3a-92fd-290df6bb8908","agentId":"a80d1ba9a1945c3c3","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01SGcx7QeEHWB3HVoaH7zJK7","type":"tool_result","content":"1\t// Adaptateurs origine — famille \"produits\" (4 pages produit).\n2\t//\n3\t// Chaque adaptateur reçoit une section WordPress (champs camelCase GraphQL,\n4\t// cf. src/lib/wp/types.ts) et rend le composant ORIGINAL du site en mappant\n5\t// les champs. Valeur WP absente/null => prop undefined (le défaut du composant\n6\t// d'origine prend le relais). Quand un champ WP \"riche\" (strong, liens…) est\n7\t// identique au contenu seedé (comparaison normalisée), on laisse volontairement\n8\t// le défaut JSX riche de l'original s'appliquer pour garder la parité pixel.\n9\t//\n10\t// Clés du registry : nom du fichier composant original, suffixé par le produit\n11\t// quand le composant générique est configuré différemment par vue\n12\t// (ex. \"ProductHeroCovaTherm\", \"CompatibilityTabsCovaSeal\").\n13\timport { Fragment } from \"react\";\n14\timport type { ComponentType, ReactNode } from \"react\";\n15\timport {\n16\t  BadgeCheck,\n17\t  Banknote,\n18\t  Brain,\n19\t  Building2,\n20\t  Clock,\n21\t  Droplets,\n22\t  Eye,\n23\t  FileCheck2,\n24\t  Flame,\n25\t  Heart,\n26\t  Leaf,\n27\t  Shield,\n28\t  ShieldCheck,\n29\t  Snowflake,\n30\t  Sun,\n31\t  Thermometer,\n32\t  Zap,\n33\t} from \"lucide-react\";\n34\timport type { LucideIcon } from \"lucide-react\";\n35\t\n36\timport { arr, mapImage, parseAccent } from \"@/lib/wp/mappers\";\n37\timport type {\n38\t  AvantApresBlock,\n39\t  CertificationsBlock,\n40\t  CompatibiliteSupportsBlock,\n41\t  ContenuSeoBlock,\n42\t  FaqBlock,\n43\t  GrilleCardsBlock,\n44\t  GrilleSolutionsBlock,\n45\t  HeroBlock,\n46\t  SpecsTechniquesBlock,\n47\t  TableauComparatifBlock,\n48\t  TexteImageBlock,\n49\t  VariantesProduitBlock,\n50\t} from \"@/lib/wp/types\";\n51\t\n52\timport ProductHero from \"@/components/product/ProductHero\";\n53\timport ProductShowcase, { defaultVariants } from \"@/components/product/ProductShowcase\";\n54\timport CompatibilityTabs from \"@/components/product/CompatibilityTabs\";\n55\timport TechnicalSpecs from \"@/components/product/TechnicalSpecs\";\n56\timport ProductAdvantages from \"@/components/product/ProductAdvantages\";\n57\timport ProductFAQ from \"@/components/product/ProductFAQ\";\n58\timport CoolRoofGuide from \"@/components/product/CoolRoofGuide\";\n59\timport RelatedProducts, { allProducts } from \"@/components/product/RelatedProducts\";\n60\timport type { RelatedProductItem } from \"@/components/product/RelatedProducts\";\n61\t\n62\timport CovaSealShowcase from \"@/components/product/covaseal/CovaSealShowcase\";\n63\timport CovaSealSpecs from \"@/components/product/covaseal/CovaSealSpecs\";\n64\timport CovaSealCertifications from \"@/components/product/covaseal/CovaSealCertifications\";\n65\timport CovaSealAdvantages from \"@/components/product/covaseal/CovaSealAdvantages\";\n66\timport CovaSealComparison from \"@/components/product/covaseal/CovaSealComparison\";\n67\timport CovaSealEditorial from \"@/components/product/covaseal/CovaSealEditorial\";\n68\t\n69\timport CovaMetalShowcase from \"@/components/product/covametal/CovaMetalShowcase\";\n70\timport CovaMetalSpecs from \"@/components/product/covametal/CovaMetalSpecs\";\n71\timport CovaMetalCertifications from \"@/components/product/covametal/CovaMetalCertifications\";\n72\timport CovaMetalAdvantages from \"@/components/product/covametal/CovaMetalAdvantages\";\n73\timport CovaMetalComparison from \"@/components/product/covametal/CovaMetalComparison\";\n74\timport CovaMetalEditorial from \"@/components/product/covametal/CovaMetalEditorial\";\n75\t\n76\timport CovaThermLightShowcase from \"@/components/product/covatherm-light/CovaThermLightShowcase\";\n77\timport CovaThermLightBeforeAfter from \"@/components/product/covatherm-light/CovaThermLightBeforeAfter\";\n78\timport CovaThermLightSpecs from \"@/components/product/covatherm-light/CovaThermLightSpecs\";\n79\timport CovaThermLightCertifications from \"@/components/product/covatherm-light/CovaThermLightCertifications\";\n80\timport CovaThermLightAdvantages from \"@/components/product/covatherm-light/CovaThermLightAdvantages\";\n81\timport CovaThermLightComparison from \"@/components/product/covatherm-light/CovaThermLightComparison\";\n82\timport CovaThermLightEditorial from \"@/components/product/covatherm-light/CovaThermLightEditorial\";\n83\t\n84\timport {\n85\t  covathermFaqs,\n86\t  covathermLightFaqs,\n87\t  covasealFaqs,\n88\t  covametalFaqs,\n89\t} from \"@/data/productFaqs\";\n90\timport type { ProductFaqEntry } from \"@/data/productFaqs\";\n91\t\n92\t// Assets de fallback (identiques aux imports des vues d'origine)\n93\timport covathermHero from \"@/assets/produits/covatherm-hero.webp\";\n94\timport covathermLightHero from \"@/assets/produits/covatherm-light-hero.webp\";\n95\timport covasealHero from \"@/assets/produits/covaseal-hero.webp\";\n96\timport covametalHero from \"@/assets/produits/covametal-hero.webp\";\n97\timport compatBitume from \"@/assets/produits/compat-bitume.webp\";\n98\timport compatBeton from \"@/assets/produits/compat-beton.webp\";\n99\timport compatEpdm from \"@/assets/produits/compat-epdm.webp\";\n100\timport compatFibrociment from \"@/assets/produits/compat-fibrociment.webp\";\n101\timport compatArdoise from \"@/assets/produits/compat-ardoise.webp\";\n102\timport compatPvc from \"@/assets/produits/compat-pvc.webp\";\n103\timport compatTuile from \"@/assets/produits/compat-tuile.webp\";\n104\timport compatPolycarbonate from \"@/assets/produits/compat-polycarbonate.webp\";\n105\timport compatPolyester from \"@/assets/produits/compat-polyester.webp\";\n106\timport compatFibreVerre from \"@/assets/produits/compat-fibre-verre.webp\";\n107\timport compatVitrage from \"@/assets/produits/compat-vitrage.webp\";\n108\timport compatBacAcier from \"@/assets/produits/compat-bac-acier.webp\";\n109\timport compatToleOndulee from \"@/assets/produits/compat-tole-ondulee.webp\";\n110\timport compatZinc from \"@/assets/produits/compat-zinc.webp\";\n111\timport compatMetalGenerique from \"@/assets/produits/compat-metal-generique.webp\";\n112\t\n113\ttype Section<T> = T & { position?: number };\n114\t\n115\t// ---------------------------------------------------------------------------\n116\t// Helpers\n117\t// ---------------------------------------------------------------------------\n118\t\n119\t/** Normalise un texte WP (tags HTML, entités, espaces) pour comparaison. */\n120\tfunction norm(s: string): string {\n121\t  return s\n122\t    .replace(/&nbsp;/g, \" \")\n123\t    .replace(/&rsquo;|&#8217;|’/g, \"'\") // apostrophe wptexturize -> droite (seed)\n124\t    .replace(/&amp;/g, \"&\")\n125\t    .replace(/<[^>]+>/g, \" \")\n126\t    .replace(/\\s+/g, \" \")\n127\t    .trim();\n128\t}\n129\t\n130\t/** Hash djb2 (mêmes valeurs que le calcul de référence sur les seeds). */\n131\tfunction hash(s: string): number {\n132\t  let h = 5381;\n133\t  for (let i = 0; i < s.length; i++) {\n134\t    h = ((h * 33) ^ s.charCodeAt(i)) >>> 0;\n135\t  }\n136\t  return h;\n137\t}\n138\t\n139\t/**\n140\t * Retourne la valeur WP, ou undefined si elle est vide ou identique\n141\t * (après normalisation) au contenu d'origine — le défaut riche du composant\n142\t * (strong, liens stylés…) prend alors le relais.\n143\t */\n144\tfunction richOr(value: string | null | undefined, normalizedDefault: string): string | undefined {\n145\t  if (!value) return undefined;\n146\t  return norm(value) === normalizedDefault ? undefined : value;\n147\t}\n148\t\n149\t/** Supprime le <p> englobant d'un wysiwyg mono-paragraphe. */\n150\tfunction stripOuterP(html: string): string {\n151\t  return html.trim().replace(/^<p>/i, \"\").replace(/<\\/p>$/i, \"\");\n152\t}\n153\t\n154\t/**\n155\t * Reconstruit le markup de titre des originaux à partir de la convention\n156\t * `*accent*` : segment accentué => <span className={accentClass}>, précédé\n157\t * d'un <br /> (sauf opts.br === false, pour les titres dont le span est sur\n158\t * la même ligne).\n159\t */\n160\tfunction accentNode(\n161\t  titre: string | null | undefined,\n162\t  accentClass: string,\n163\t  opts: { br?: boolean } = {},\n164\t): ReactNode | undefined {\n165\t  if (!titre) return undefined;\n166\t  const segments = parseAccent(titre);\n167\t  if (!segments.some((s) => s.accent)) return titre;\n168\t  return (\n169\t    <>\n170\t      {segments.map((seg, i) =>\n171\t        seg.accent ? (\n172\t          <Fragment key={i}>\n173\t            {opts.br !== false && i > 0 && <br />}\n174\t            <span className={accentClass}>{seg.text}</span>\n175\t          </Fragment>\n176\t        ) : (\n177\t          <Fragment key={i}>{seg.text}</Fragment>\n178\t        ),\n179\t      )}\n180\t    </>\n181\t  );\n182\t}\n183\t\n184\t/** Cellule de tableau specs : \"oui\"/\"non\"/\"-\" => boolean/null, sinon texte. */\n185\tfunction cellValue(t: string | null | undefined): string | boolean | null {\n186\t  if (t == null) return null;\n187\t  const v = t.trim();\n188\t  if (v === \"\" || v === \"-\") return null;\n189\t  if (v.toLowerCase() === \"oui\") return true;\n190\t  if (v.toLowerCase() === \"non\") return false;\n191\t  return t;\n192\t}\n193\t\n194\t/** Icônes des cards `grille_cards` (slug WP -> icône lucide de l'original). */\n195\tconst CARD_ICONS: Record<string, LucideIcon> = {\n196\t  euro: Banknote,\n197\t  banknote: Banknote,\n198\t  users: Heart,\n199\t  heart: Heart,\n200\t  zap: Zap,\n201\t  thermometer: Thermometer,\n202\t  \"thermometer-snowflake\": Thermometer,\n203\t  shield: Shield,\n204\t  \"shield-check\": Shield,\n205\t  sun: Sun,\n206\t  clock: Clock,\n207\t  droplets: Droplets,\n208\t  snowflake: Snowflake,\n209\t  brain: Brain,\n210\t  \"thermometer-sun\": Brain,\n211\t  eye: Eye,\n212\t};\n213\t\n214\t/** Icônes des certifications (nom WP -> icône lucide de l'original). */\n215\tconst CERT_ICONS: Record<string, LucideIcon> = {\n216\t  \"Broof(t3)\": Flame,\n217\t  \"Avis technique CSTB\": FileCheck2,\n218\t  RE2020: Building2,\n219\t  \"Décret tertiaire\": BadgeCheck,\n220\t  \"Prime CEE BAT-EN-112\": Droplets,\n221\t  \"Formulation faible COV\": Leaf,\n222\t  \"Filtration UV certifiée\": Sun,\n223\t  \"Formulée en France\": ShieldCheck,\n224\t};\n225\t\n226\t// ---------------------------------------------------------------------------\n227\t// ProductHero — l'adaptateur reproduit l'appel exact de chaque vue d'origine\n228\t// ---------------------------------------------------------------------------\n229\t\n230\tinterface HeroDefaults {\n231\t  tag: string;\n232\t  productName: string;\n233\t  tagline: string;\n234\t  description: string;\n235\t  heroImage: string;\n236\t  heroImageAlt?: string;\n237\t  accentColor: string;\n238\t  primaryCTA: { label: string; href: string };\n239\t  secondaryCTA: { label: string; href: string };\n240\t  inlineBadges: string[];\n241\t  heroStats: { value: string; label: string }[];\n242\t}\n243\t\n244\tconst makeProductHero = (d: HeroDefaults) =>\n245\t  function ProductHeroAdapter(s: Section<HeroBlock>) {\n246\t    const img = mapImage(s.image);\n247\t    const badges = arr(s.badges)\n248\t      .map((b) => b.texte ?? \"\")\n249\t      .filter(Boolean);\n250\t    const stats = arr(s.stats)\n251\t      .filter((st) => st.value || st.label)\n252\t      .map((st) => ({ value: st.value ?? \"\", label: st.label ?? \"\" }));\n253\t    return (\n254\t      <ProductHero\n255\t        tag={s.eyebrow ?? d.tag}\n256\t        productName={s.titre ?? d.productName}\n257\t        tagline={d.tagline}\n258\t        description={s.lead ?? d.description}\n259\t        heroImage={img?.sourceUrl ?? d.heroImage}\n260\t        heroImageAlt={img?.altText || d.heroImageAlt}\n261\t        accentColor={d.accentColor}\n262\t        primaryCTA={\n263\t          s.ctaPrimaire?.label\n264\t            ? { label: s.ctaPrimaire.label, href: s.ctaPrimaire.lien ?? d.primaryCTA.href }\n265\t            : d.primaryCTA\n266\t        }\n267\t        secondaryCTA={\n268\t          s.ctaSecondaire?.label\n269\t            ? { label: s.ctaSecondaire.label, href: s.ctaSecondaire.lien ?? d.secondaryCTA.href }\n270\t            : d.secondaryCTA\n271\t        }\n272\t        inlineBadges={badges.length ? badges : d.inlineBadges}\n273\t        heroStats={stats.length ? stats : d.heroStats}\n274\t      />\n275\t    );\n276\t  };\n277\t\n278\tconst ProductHeroCovaTherm = makeProductHero({\n279\t  tag: \"Peinture cool roof professionnelle\",\n280\t  productName: \"CovaTherm\",\n281\t  tagline: \"La peinture cool roof la plus durable du marché.\",\n282\t  description:\n283\t    \"La peinture blanche réfléchissante haute performance signée Covalba, pionnier français du cool roof depuis 2018. Résine polyuréthane, SRI 118, garantie 20 ans, pour les toitures industrielles et tertiaires les plus exigeantes.\",\n284\t  heroImage: covathermHero.src,\n285\t  accentColor: \"#038d92\",\n286\t  primaryCTA: { label: \"Demander un devis\", href: \"/diagnostic\" },\n287\t  secondaryCTA: { label: \"Télécharger la fiche technique\", href: \"#fiche-technique\" },\n288\t  inlineBadges: [\"SRI 119\", \"20 ans de garantie\", \"Prime CEE éligible\"],\n289\t  heroStats: [\n290\t    { value: \"-10°C\", label: \"Température intérieure\" },\n291\t    { value: \"40%\", label: \"d'économie d'énergie\" },\n292\t    { value: \"20 ans\", label: \"de durabilité\" },\n293\t  ],\n294\t});\n295\t\n296\tconst ProductHeroCovaThermLight = makeProductHero({\n297\t  tag: \"Peinture anti-UV pour polycarbonate\",\n298\t  productName: \"CovaTherm Light\",\n299\t  tagline: \"La laque solaire qui bloque la chaleur sans voler la lumière.\",\n300\t  description:\n301\t    \"Revêtement acrylique blanc haute réflectivité, signé Covalba, pionnier français du cool roof depuis 2018. Appliquée sur vos lanterneaux, voûtes éclairantes et puits de lumière en polycarbonate ou polyester, elle filtre les UV, les infrarouges et l'éblouissement, tout en préservant jusqu'à 65 % de la luminosité naturelle.\",\n302\t  heroImage: covathermLightHero.src,\n303\t  heroImageAlt: \"Application de CovaTherm Light peinture anti-UV sur lanterneau polycarbonate\",\n304\t  accentColor: \"#0d3840\",\n305\t  primaryCTA: { label: \"Demander un devis\", href: \"/diagnostic\" },\n306\t  secondaryCTA: { label: \"Télécharger la fiche technique\", href: \"#specs\" },\n307\t  inlineBadges: [\"98 % des UV filtrés\", \"65 % de lumière préservée\", \"5 à 8 ans\"],\n308\t  heroStats: [\n309\t    { value: \"-5°C\", label: \"Sous les lanterneaux\" },\n310\t    { value: \"65 %\", label: \"de lumière préservée\" },\n311\t    { value: \"10 à 20 %\", label: \"de clim économisée\" },\n312\t  ],\n313\t});\n314\t\n315\tconst ProductHeroCovaSeal = makeProductHero({\n316\t  tag: \"Étanchéité cool roof professionnelle\",\n317\t  productName: \"CovaSeal 20\",\n318\t  tagline: \"L'étanchéité liquide cool roof 2-en-1.\",\n319\t  description:\n320\t    \"Résine acrylique élastomère avec Top Coat anti-UV, signée Covalba, pionnier français du cool roof depuis 2018. Une seule application pour restaurer l'étanchéité de votre toiture et gagner jusqu'à -10°C en intérieur, avec une garantie 20 ans.\",\n321\t  heroImage: covasealHero.src,\n322\t  heroImageAlt: \"Application de CovaSeal 20 étanchéité cool roof au rouleau sur toiture industrielle\",\n323\t  accentColor: \"#e18300\",\n324\t  primaryCTA: { label: \"Demander un devis\", href: \"/diagnostic\" },\n325\t  secondaryCTA: { label: \"Télécharger la fiche technique\", href: \"#specs\" },\n326\t  inlineBadges: [\"SRI 107\", \"20 ans de garantie\", \"Prime CEE éligible\"],\n327\t  heroStats: [\n328\t    { value: \"-8 à -10°C\", label: \"Température intérieure\" },\n329\t    { value: \"20 ans\", label: \"d'étanchéité + cool roof\" },\n330\t    { value: \"Prime CEE\", label: \"Éligible sous conditions\" },\n331\t  ],\n332\t});\n333\t\n334\tconst ProductHeroCovaMetal = makeProductHero({\n335\t  tag: \"Peinture cool roof bac acier professionnelle\",\n336\t  productName: \"CovaMetal 20\",\n337\t  tagline: \"La peinture cool roof 3-en-1 pour toitures métalliques corrodées.\",\n338\t  description:\n339\t    \"Résine élastomère avec Top Coat anti-UV, signée Covalba, pionnier français du cool roof depuis 2018. Une seule application pour traiter la corrosion, restaurer l'étanchéité et gagner jusqu'à -10°C en intérieur, avec une garantie 20 ans.\",\n340\t  heroImage: covametalHero.src,\n341\t  heroImageAlt: \"Application de CovaMetal 20 peinture cool roof sur toiture bac acier corrodé\",\n342\t  accentColor: \"#a9272e\",\n343\t  primaryCTA: { label: \"Demander un devis\", href: \"/diagnostic\" },\n344\t  secondaryCTA: { label: \"Télécharger la fiche technique\", href: \"#specs\" },\n345\t  inlineBadges: [\"SRI 107\", \"20 ans de garantie\", \"Prime CEE éligible\"],\n346\t  heroStats: [\n347\t    { value: \"-8 à -10°C\", label: \"Température intérieure\" },\n348\t    { value: \"20 ans\", label: \"d'anti-corrosion + cool roof\" },\n349\t    { value: \"Prime CEE\", label: \"Éligible sous conditions\" },\n350\t  ],\n351\t});\n352\t\n353\t// ---------------------------------------------------------------------------\n354\t// ProductShowcase (variantes_produit, CovaTherm)\n355\t// ---------------------------------------------------------------------------\n356\t\n357\tconst SHOWCASE_INTRO_COVATHERM =\n358\t  \"Deux peintures cool roof polyuréthane qui partagent la même base technique brevetée. Seule la finition change, selon l'âge de votre toiture et votre horizon d'amortissement.\";\n359\t\n360\tfunction ProductShowcaseAdapter(s: Section<VariantesProduitBlock>) {\n361\t  const wp = arr(s.variantes);\n362\t  const variants = wp.length\n363\t    ? wp.map((v, i) => {\n364\t        const base = defaultVariants[i] ?? defaultVariants[defaultVariants.length - 1];\n365\t        return {\n366\t          ...base,\n367\t          name: v.nom ?? base.name,\n368\t          tagline: v.usage ?? base.tagline,\n369\t          description: v.description ?? base.description,\n370\t        };\n371\t      })\n372\t    : undefined;\n373\t  return (\n374\t    <ProductShowcase\n375\t      badge={s.entete?.badge ?? undefined}\n376\t      titre={accentNode(s.entete?.titre, \"text-teal-vivid\")}\n377\t      intro={richOr(s.entete?.intro, SHOWCASE_INTRO_COVATHERM)}\n378\t      variants={variants}\n379\t    />\n380\t  );\n381\t}\n382\t\n383\t// ---------------------------------------------------------------------------\n384\t// CompatibilityTabs (compatibilite_supports) — un adaptateur par produit\n385\t// ---------------------------------------------------------------------------\n386\t\n387\tinterface CompatRoof {\n388\t  type: string;\n389\t  detail: string;\n390\t  incompatible?: boolean;\n391\t  image?: string;\n392\t}\n393\t\n394\tinterface CompatDefaults {\n395\t  productName: string;\n396\t  fallbackRoofs: CompatRoof[];\n397\t  introNode?: ReactNode;\n398\t  introText?: string;\n399\t  coverageNote?: string;\n400\t  footerNode?: ReactNode;\n401\t  footerText?: string;\n402\t  incompatibleTypes?: string[];\n403\t}\n404\t\n405\tconst makeCompatibilityTabs = (d: CompatDefaults) =>\n406\t  function CompatibilityTabsAdapter(s: Section<CompatibiliteSupportsBlock>) {\n407\t    const roofs: CompatRoof[] = arr(s.supports).map((r) => {\n408\t      const img = mapImage(r.image);\n409\t      return {\n410\t        type: r.type ?? \"\",\n411\t        detail: r.detail ?? \"\",\n412\t        image: img?.sourceUrl,\n413\t        incompatible: d.incompatibleTypes?.includes(r.type ?? \"\") || undefined,\n414\t      };\n415\t    });\n416\t    // note_couverture : 1re ligne = note de couverture, suite = note de pied.\n417\t    const lines = (s.noteCouverture ?? \"\")\n418\t      .split(\"\\n\")\n419\t      .map((l) => l.trim())\n420\t      .filter(Boolean);\n421\t    const wpFooter = lines.slice(1).join(\" \");\n422\t    const wpIntro = s.entete?.intro;\n423\t    return (\n424\t      <CompatibilityTabs\n425\t        productName={d.productName}\n426\t        compatibleRoofs={roofs.length ? roofs : d.fallbackRoofs}\n427\t        badge={s.entete?.badge ?? undefined}\n428\t        titre={accentNode(s.entete?.titre, \"text-foreground/45\")}\n429\t        intro={\n430\t          wpIntro\n431\t            ? d.introText && norm(wpIntro) === d.introText\n432\t              ? d.introNode\n433\t              : <p>{wpIntro}</p>\n434\t            : d.introNode\n435\t        }\n436\t        coverageNote={lines[0] ?? d.coverageNote}\n437\t        footerNote={\n438\t          wpFooter\n439\t            ? d.footerText && norm(wpFooter) === d.footerText\n440\t              ? d.footerNode\n441\t              : wpFooter\n442\t            : d.footerNode\n443\t        }\n444\t      />\n445\t    );\n446\t  };\n447\t\n448\tconst CompatibilityTabsCovaTherm = makeCompatibilityTabs({\n449\t  productName: \"CovaTherm\",\n450\t  fallbackRoofs: [\n451\t    {\n452\t      type: \"Membrane bitumineuse\",\n453\t      detail: \"Toiture plate ou légèrement en pente, en bon état général. 80% de nos chantiers.\",\n454\t      image: compatBitume.src,\n455\t    },\n456\t    {\n457\t      type: \"Béton / dalle béton\",\n458\t      detail: \"Surface solide, non fissurée. Adhérence parfaite sur béton brut ou peint.\",\n459\t      image: compatBeton.src,\n460\t    },\n461\t    {\n462\t      type: \"EPDM (caoutchouc)\",\n463\t      detail: \"Membrane EPDM en bon état. Préparation de surface par notre technicien.\",\n464\t      image: compatEpdm.src,\n465\t    },\n466\t    {\n467\t      type: \"Fibrociment\",\n468\t      detail: \"Surface poreuse compatible, sans fibres apparentes. Bilan technique obligatoire.\",\n469\t      image: compatFibrociment.src,\n470\t    },\n471\t    {\n472\t      type: \"Ardoise et tuile\",\n473\t      detail: \"Toits légèrement inclinés, jusqu'à 15° de pente.\",\n474\t      image: compatArdoise.src,\n475\t    },\n476\t    {\n477\t      type: \"PVC et membrane synthétique\",\n478\t      detail: \"Toiture PVC en bon état. Test d'adhérence préalable.\",\n479\t      image: compatPvc.src,\n480\t    },\n481\t  ],\n482\t  introNode: (\n483\t    <p>\n484\t      Une peinture cool roof ne tient que si elle adhère durablement au support. Chaque chantier démarre par un <strong className=\"text-foreground\">diagnostic préalable</strong> confié à un <strong className=\"text-foreground\">applicateur certifié Covalba</strong>.\n485\t    </p>\n486\t  ),\n487\t  introText:\n488\t    \"Une peinture cool roof ne tient que si elle adhère durablement au support. Chaque chantier démarre par un diagnostic préalable confié à un applicateur certifié Covalba.\",\n489\t  coverageNote:\n490\t    \"80% des toitures industrielles et tertiaires françaises sont en membrane bitumineuse.\",\n491\t  footerNode: (\n492\t    <>\n493\t      Toiture en bac acier ? Voir notre{' '}\n494\t      <a href=\"/solutions/covametal-20\" className=\"text-primary font-semibold hover:underline\">\n495\t        peinture cool roof pour bac acier\n496\t      </a>{' '}\n497\t      dédiée, CovaMetal 20.\n498\t    </>\n499\t  ),\n500\t  footerText:\n501\t    \"Toiture en bac acier ? Voir notre peinture cool roof pour bac acier dédiée, CovaMetal 20.\",\n502\t});\n503\t\n504\tconst CompatibilityTabsCovaThermLight = makeCompatibilityTabs({\n505\t  productName: \"CovaTherm Light\",\n506\t  fallbackRoofs: [\n507\t    {\n508\t      type: \"Polycarbonate\",\n509\t      detail:\n510\t        \"Matériau majoritaire sur les lanterneaux et voûtes éclairantes modernes, très sensible aux UV. Cœur de cible.\",\n511\t      image: compatPolycarbonate.src,\n512\t    },\n513\t    {\n514\t      type: \"Polyester translucide\",\n515\t      detail: \"Utilisé sur les voûtes éclairantes et certains skydômes anciens.\",\n516\t      image: compatPolyester.src,\n517\t    },\n518\t    {\n519\t      type: \"Fibre de verre translucide\",\n520\t      detail: \"Compatibilité validée après diagnostic technique sur site.\",\n521\t      image: compatFibreVerre.src,\n522\t    },\n523\t    {\n524\t      type: \"Vitrage\",\n525\t      detail:\n526\t        \"Verre, verrière, véranda : un film solaire intérieur collé est la solution adaptée, pas la laque solaire.\",\n527\t      incompatible: true,\n528\t      image: compatVitrage.src,\n529\t    },\n530\t  ],\n531\t  introNode: (\n532\t    <p>\n533\t      Une laque solaire ne tient sa promesse que si elle adhère durablement au\n534\t      support translucide sans opacifier sa lumière. Chaque chantier démarre par\n535\t      un <strong className=\"text-foreground\">diagnostic préalable</strong> confié\n536\t      à un <strong className=\"text-foreground\">applicateur certifié Covalba</strong>.\n537\t    </p>\n538\t  ),\n539\t  introText:\n540\t    \"Une laque solaire ne tient sa promesse que si elle adhère durablement au support translucide sans opacifier sa lumière. Chaque chantier démarre par un diagnostic préalable confié à un applicateur certifié Covalba.\",\n541\t  coverageNote:\n542\t    \"CovaTherm Light est formulée pour les matériaux translucides utilisés en couverture de bâtiment.\",\n543\t  footerNode: (\n544\t    <>\n545\t      Pour la partie opaque de votre toiture, voir notre{' '}\n546\t      <a href=\"/solutions/covatherm\" className=\"text-primary font-semibold hover:underline\">\n547\t        peinture cool roof CovaTherm\n548\t      </a>\n549\t      .\n550\t    </>\n551\t  ),\n552\t  footerText: \"Pour la partie opaque de votre toiture, voir notre peinture cool roof CovaTherm.\",\n553\t  incompatibleTypes: [\"Vitrage\"],\n554\t});\n555\t\n556\tconst CompatibilityTabsCovaSeal = makeCompatibilityTabs({\n557\t  productName: \"CovaSeal 20\",\n558\t  fallbackRoofs: [\n559\t    {\n560\t      type: \"Étanchéité bitumineuse\",\n561\t      detail: \"Membrane bitumineuse vieillissante, posée depuis plus d'un an.\",\n562\t      image: compatBitume.src,\n563\t    },\n564\t    {\n565\t      type: \"Membrane EPDM\",\n566\t      detail: \"Membrane caoutchouc EPDM de plus de 10 ans, avec test d'adhérence préalable.\",\n567\t      image: compatEpdm.src,\n568\t    },\n569\t    {\n570\t      type: \"Membrane PVC\",\n571\t      detail:\n572\t        \"Membrane synthétique PVC de plus de 10 ans. Préparation de surface par notre technicien.\",\n573\t      image: compatPvc.src,\n574\t    },\n575\t    {\n576\t      type: \"Tuiles\",\n577\t      detail: \"Tuiles terre cuite ou béton, légèrement inclinées, en bon état structurel.\",\n578\t      image: compatTuile.src,\n579\t    },\n580\t    {\n581\t      type: \"Ardoises\",\n582\t      detail: \"Toitures ardoise avec pente modérée, sans casses majeures.\",\n583\t      image: compatArdoise.src,\n584\t    },\n585\t    {\n586\t      type: \"Fibrociment\",\n587\t      detail: \"Surface poreuse compatible, sans fibres apparentes. Bilan technique obligatoire.\",\n588\t      image: compatFibrociment.src,\n589\t    },\n590\t  ],\n591\t  coverageNote:\n592\t    \"Un enduit d'étanchéité cool roof ne tient sa promesse que s'il adhère durablement au support.\",\n593\t});\n594\t\n595\tconst CompatibilityTabsCovaMetal = makeCompatibilityTabs({\n596\t  productName: \"CovaMetal 20\",\n597\t  fallbackRoofs: [\n598\t    {\n599\t      type: \"Bac acier\",\n600\t      detail: \"Plaques galvanisées, peintes ou brutes, avec ou sans corrosion. Cœur de cible.\",\n601\t      image: compatBacAcier.src,\n602\t    },\n603\t    {\n604\t      type: \"Tôle ondulée métallique\",\n605\t      detail: \"Tôles ondulées ou nervurées, avec ou sans corrosion de surface.\",\n606\t      image: compatToleOndulee.src,\n607\t    },\n608\t    {\n609\t      type: \"Zinc\",\n610\t      detail: \"Toitures en zinc vieillissantes, adhérence vérifiée par test préalable.\",\n611\t      image: compatZinc.src,\n612\t    },\n613\t    {\n614\t      type: \"Autres supports métalliques\",\n615\t      detail: \"Compatibilité validée après diagnostic technique sur site.\",\n616\t      image: compatMetalGenerique.src,\n617\t    },\n618\t  ],\n619\t  coverageNote:\n620\t    \"Un revêtement cool roof anti-corrosion ne tient sa promesse que s'il adhère durablement au support métallique.\",\n621\t});\n622\t\n623\t// ---------------------------------------------------------------------------\n624\t// Specs techniques (specs_techniques)\n625\t// ---------------------------------------------------------------------------\n626\t\n627\tconst SPECS_INTRO_COVATHERM =\n628\t  \"Performances thermiques identiques à l'application. La différence se joue dans la durée : CovaTherm 8 tient 8 à 10 ans, CovaTherm 20 pousse à 20 ans grâce à sa finition Top Coat anti-UV. Loin des peintures cool roof acryliques du marché, qui tiennent 2 à 5 ans.\";\n629\t\n630\tfunction TechnicalSpecsAdapter(s: Section<SpecsTechniquesBlock>) {\n631\t  const cols = arr(s.colonnesSpecs);\n632\t  const lignes = arr(s.lignesSpecs);\n633\t  const specs = lignes.length\n634\t    ? lignes.map((r) => ({\n635\t        label: r.label ?? \"\",\n636\t        v8: cellValue(r.valeurs?.[0]?.texte),\n637\t        v20: cellValue(r.valeurs?.[1]?.texte),\n638\t      }))\n639\t    : undefined;\n640\t  return (\n641\t    <TechnicalSpecs\n642\t      badge={s.entete?.badge ?? undefined}\n643\t      titre={accentNode(s.entete?.titre, \"text-foreground/45\")}\n644\t      intro={richOr(s.entete?.intro, SPECS_INTRO_COVATHERM)}\n645\t      specs={specs}\n646\t      nomCol8={cols[0]?.nom ?? undefined}\n647\t      nomCol20={cols[1]?.nom ?? undefined}\n648\t    />\n649\t  );\n650\t}\n651\t\n652\tconst makeSpecs = (\n653\t  Comp: ComponentType<{\n654\t    badge?: string;\n655\t    titre?: ReactNode;\n656\t    intro?: ReactNode;\n657\t    specs?: { label: string; value: string | boolean | null }[];\n658\t    nomColonne?: string;\n659\t  }>,\n660\t) =>\n661\t  function SpecsAdapter(s: Section<SpecsTechniquesBlock>) {\n662\t    const cols = arr(s.colonnesSpecs);\n663\t    const lignes = arr(s.lignesSpecs);\n664\t    const specs = lignes.length\n665\t      ? lignes.map((r) => ({\n666\t          label: r.label ?? \"\",\n667\t          value: cellValue(r.valeurs?.[0]?.texte),\n668\t        }))\n669\t      : undefined;\n670\t    return (\n671\t      <Comp\n672\t        badge={s.entete?.badge ?? undefined}\n673\t        titre={accentNode(s.entete?.titre, \"text-foreground/45\")}\n674\t        intro={s.entete?.intro ?? undefined}\n675\t        specs={specs}\n676\t        nomColonne={cols[0]?.nom ?? undefined}\n677\t      />\n678\t    );\n679\t  };\n680\t\n681\tconst CovaSealSpecsAdapter = makeSpecs(CovaSealSpecs);\n682\tconst CovaMetalSpecsAdapter = makeSpecs(CovaMetalSpecs);\n683\tconst CovaThermLightSpecsAdapter = makeSpecs(CovaThermLightSpecs);\n684\t\n685\t// ---------------------------------------------------------------------------\n686\t// Avantages (grille_cards)\n687\t// ---------------------------------------------------------------------------\n688\t\n689\tconst makeAdvantages = (\n690\t  Comp: ComponentType<{\n691\t    badge?: string;\n692\t    titre?: ReactNode;\n693\t    intro?: ReactNode;\n694\t    advantages?: { icon: LucideIcon; title: string; desc: string }[];\n695\t  }>,\n696\t  introDefault: string,\n697\t  iconOverrides: Record<string, LucideIcon> = {},\n698\t) =>\n699\t  function AdvantagesAdapter(s: Section<GrilleCardsBlock>) {\n700\t    const cards = arr(s.cards);\n701\t    const advantages = cards.length\n702\t      ? cards.map((c) => ({\n703\t          icon: iconOverrides[c.icone ?? \"\"] ?? CARD_ICONS[c.icone ?? \"\"] ?? BadgeCheck,\n704\t          title: c.titre ?? \"\",\n705\t          desc: c.texte ?? \"\",\n706\t        }))\n707\t      : undefined;\n708\t    return (\n709\t      <Comp\n710\t        badge={s.entete?.badge ?? undefined}\n711\t        titre={accentNode(s.entete?.titre, \"text-teal-vivid\", { br: false })}\n712\t        intro={richOr(s.entete?.intro, introDefault)}\n713\t        advantages={advantages}\n714\t      />\n715\t    );\n716\t  };\n717\t\n718\tconst ProductAdvantagesAdapter = makeAdvantages(\n719\t  ProductAdvantages,\n720\t  \"Appliquer une peinture cool roof sur une toiture professionnelle produit des effets mesurables dès le premier été. Six bénéfices constatés sur nos chantiers, chiffres à l'appui.\",\n721\t);\n722\tconst CovaSealAdvantagesAdapter = makeAdvantages(\n723\t  CovaSealAdvantages,\n724\t  \"Appliquer CovaSeal 20 sur une toiture fatiguée produit un double effet dès le premier été : l'étanchéité est restaurée et la température intérieure baisse de façon mesurable. Voici les six bénéfices constatés sur nos chantiers, chiffres à l'appui.\",\n725\t);\n726\tconst CovaMetalAdvantagesAdapter = makeAdvantages(\n727\t  CovaMetalAdvantages,\n728\t  \"Appliquer CovaMetal 20 sur une toiture métallique corrodée produit un triple effet dès la première saison : la corrosion est stoppée, l'étanchéité est restaurée, et la température intérieure baisse de façon mesurable. Voici les six bénéfices constatés sur nos chantiers.\",\n729\t);\n730\tconst CovaThermLightAdvantagesAdapter = makeAdvantages(\n731\t  CovaThermLightAdvantages,\n732\t  \"Appliquer CovaTherm Light sur vos lanterneaux transforme l'ambiance intérieure de votre bâtiment dès le premier été : la chaleur radiante disparaît, l'éblouissement cesse, mais la lumière naturelle reste. Voici les six bénéfices constatés sur nos chantiers.\",\n733\t  // Mapping spécifique CovaTherm Light (icônes d'origine : Brain et Eye)\n734\t  { \"thermometer-sun\": Brain, \"shield-check\": Eye },\n735\t);\n736\t\n737\t// ---------------------------------------------------------------------------\n738\t// Certifications (certifications)\n739\t// ---------------------------------------------------------------------------\n740\t\n741\tconst makeCertifications = (\n742\t  Comp: ComponentType<{\n743\t    badge?: string;\n744\t    titre?: ReactNode;\n745\t    intro?: ReactNode;\n746\t    certifications?: { icon: LucideIcon; label: string; desc: string }[];\n747\t  }>,\n748\t) =>\n749\t  function CertificationsAdapter(s: Section<CertificationsBlock>) {\n750\t    const items = arr(s.itemsCertifications);\n751\t    const certifications = items.length\n752\t      ? items.map((c) => ({\n753\t          icon: CERT_ICONS[c.nom ?? \"\"] ?? BadgeCheck,\n754\t          label: c.nom ?? \"\",\n755\t          desc: c.description ?? \"\",\n756\t        }))\n757\t      : undefined;\n758\t    return (\n759\t      <Comp\n760\t        badge={s.entete?.badge ?? undefined}\n761\t        titre={accentNode(s.entete?.titre, \"text-foreground/45\")}\n762\t        intro={s.entete?.intro ?? undefined}\n763\t        certifications={certifications}\n764\t      />\n765\t    );\n766\t  };\n767\t\n768\tconst CovaSealCertificationsAdapter = makeCertifications(CovaSealCertifications);\n769\tconst CovaMetalCertificationsAdapter = makeCertifications(CovaMetalCertifications);\n770\tconst CovaThermLightCertificationsAdapter = makeCertifications(CovaThermLightCertifications);\n771\t\n772\t// ---------------------------------------------------------------------------\n773\t// Comparatifs (tableau_comparatif)\n774\t// ---------------------------------------------------------------------------\n775\t\n776\tconst makeComparison = (\n777\t  Comp: ComponentType<{\n778\t    badge?: string;\n779\t    titre?: ReactNode;\n780\t    intro?: ReactNode;\n781\t    rows?: { label: string; a: string; b: string; c?: string }[];\n782\t    colA?: string;\n783\t    colB?: string;\n784\t    colC?: string;\n785\t    texteApres?: ReactNode;\n786\t  }>,\n787\t  texteApresDefault: string,\n788\t  withC: boolean,\n789\t) =>\n790\t  function ComparisonAdapter(s: Section<TableauComparatifBlock>) {\n791\t    const cols = arr(s.entetesColonnes).map((c) => c.texte ?? \"\");\n792\t    const lignes = arr(s.lignesComparatif);\n793\t    const rows = lignes.length\n794\t      ? lignes.map((l) => {\n795\t          const cells = arr(l.cellules).map((x) => x.texte ?? \"\");\n796\t          return withC\n797\t            ? { label: cells[0] ?? \"\", a: cells[1] ?? \"\", b: cells[2] ?? \"\", c: cells[3] ?? \"\" }\n798\t            : { label: cells[0] ?? \"\", a: cells[1] ?? \"\", b: cells[2] ?? \"\" };\n799\t        })\n800\t      : undefined;\n801\t    return (\n802\t      <Comp\n803\t        badge={s.entete?.badge ?? undefined}\n804\t        titre={accentNode(s.entete?.titre, \"text-foreground/45\")}\n805\t        intro={s.entete?.intro ?? undefined}\n806\t        rows={rows}\n807\t        colA={cols[0] || undefined}\n808\t        colB={cols[1] || undefined}\n809\t        colC={withC ? cols[2] || undefined : undefined}\n810\t        texteApres={richOr(s.texteApres, texteApresDefault)}\n811\t      />\n812\t    );\n813\t  };\n814\t\n815\tconst CovaSealComparisonAdapter = makeComparison(\n816\t  CovaSealComparison,\n817\t  \"CovaSeal 20 est la solution qui s'amortit le plus rapidement, sans démontage ni interruption d'activité. Elle s'impose comme la référence pour les bâtiments industriels, pharmaceutiques, agroalimentaires, aéronautiques et tertiaires dont la toiture est fatiguée mais encore exploitable. Pour une toiture métallique type bac acier, la peinture cool roof pour bac acier CovaMetal 20 est mieux adaptée.\",\n818\t  true,\n819\t);\n820\tconst CovaMetalComparisonAdapter = makeComparison(\n821\t  CovaMetalComparison,\n822\t  \"CovaMetal 20 est la solution qui s'amortit le plus rapidement, sans démontage ni interruption d'activité. Elle s'impose comme la référence pour les bâtiments industriels, logistiques et agricoles dont la toiture bac acier est corrodée en surface mais encore structurellement exploitable. Pour une toiture non métallique (bitume, béton, membrane), la peinture étanchéité cool roof CovaSeal 20 est mieux adaptée.\",\n823\t  false,\n824\t);\n825\tconst CovaThermLightComparisonAdapter = makeComparison(\n826\t  CovaThermLightComparison,\n827\t  \"CovaTherm Light est la solution adaptée quand les lanterneaux sont encore structurellement sains mais provoquent un inconfort thermique et lumineux. Pour des voûtes éclairantes très jaunies, déformées ou en fin de vie, un remplacement ponctuel des éléments concernés est préférable avant d'envisager une laque solaire sur le reste. Pour la partie opaque de votre toiture, la peinture cool roof CovaTherm complète le traitement.\",\n828\t  false,\n829\t);\n830\t\n831\t// ---------------------------------------------------------------------------\n832\t// FAQ (faq)\n833\t// ---------------------------------------------------------------------------\n834\t\n835\tconst makeFaq = (productName: string, fallback: ProductFaqEntry[]) =>\n836\t  function FaqAdapter(s: Section<FaqBlock>) {\n837\t    const questions = arr(s.questions);\n838\t    const faqs = questions.length\n839\t      ? questions.map((q) => ({\n840\t          q: q.question ?? \"\",\n841\t          a: (\n842\t            <div\n843\t              className=\"[&_a]:text-primary [&_a]:font-semibold [&_a:hover]:underline\"\n844\t              dangerouslySetInnerHTML={{ __html: q.reponse ?? \"\" }}\n845\t            />\n846\t          ),\n847\t        }))\n848\t      : fallback.map(({ q, text }) => ({ q, a: text as ReactNode }));\n849\t    return (\n850\t      <ProductFAQ\n851\t        productName={productName}\n852\t        faqs={faqs}\n853\t        badge={s.entete?.badge ?? undefined}\n854\t        titre={s.entete?.titre ?? undefined}\n855\t        intro={s.entete?.intro ?? undefined}\n856\t        ctaLabel={s.cta?.label ?? undefined}\n857\t        ctaHref={s.cta?.lien ?? undefined}\n858\t      />\n859\t    );\n860\t  };\n861\t\n862\tconst ProductFAQCovaTherm = makeFaq(\"CovaTherm\", covathermFaqs);\n863\tconst ProductFAQCovaThermLight = makeFaq(\"CovaTherm Light\", covathermLightFaqs);\n864\tconst ProductFAQCovaSeal = makeFaq(\"CovaSeal 20\", covasealFaqs);\n865\tconst ProductFAQCovaMetal = makeFaq(\"CovaMetal 20\", covametalFaqs);\n866\t\n867\t// ---------------------------------------------------------------------------\n868\t// Contenu SEO (contenu_seo) — guide CovaTherm + dossiers éditoriaux\n869\t//\n870\t// Si le contenu WP est identique au seed (hash du texte normalisé), on rend\n871\t// le composant original sans props : le corps riche d'origine (puces custom,\n872\t// liens stylés…) garantit la parité pixel. Dès qu'un texte est édité dans le\n873\t// BO, on bascule sur le rendu HTML générique.\n874\t// ---------------------------------------------------------------------------\n875\t\n876\tconst makeContenuSeo = (\n877\t  Comp: ComponentType<{\n878\t    intro?: string;\n879\t    sections?: { titre?: string | null; contenu?: string | null }[];\n880\t    sources?: { ref: string; url: string }[];\n881\t  }>,\n882\t  seedDigest: number,\n883\t  opts: { stripIntroP?: boolean } = {},\n884\t) =>\n885\t  function ContenuSeoAdapter(s: Section<ContenuSeoBlock>) {\n886\t    const sections = arr(s.sections);\n887\t    const digest =\n888\t      norm(s.intro ?? \"\") +\n889\t      sections.map((x) => norm(x.titre ?? \"\") + norm(x.contenu ?? \"\")).join(\"\");\n890\t    if (hash(digest) === seedDigest) return <Comp />;\n891\t    const sources = arr(s.sources)\n892\t      .filter((x) => x.label)\n893\t      .map((x) => ({ ref: x.label ?? \"\", url: x.url ?? \"\" }));\n894\t    return (\n895\t      <Comp\n896\t        intro={s.intro ? (opts.stripIntroP ? stripOuterP(s.intro) : s.intro) : undefined}\n897\t        sections={sections.length ? sections : undefined}\n898\t        sources={sources.length ? sources : undefined}\n899\t      />\n900\t    );\n901\t  };\n902\t\n903\tconst CoolRoofGuideAdapter = makeContenuSeo(CoolRoofGuide, 564420437, { stripIntroP: true });\n904\tconst CovaSealEditorialAdapter = makeContenuSeo(CovaSealEditorial, 1375046801);\n905\tconst CovaMetalEditorialAdapter = makeContenuSeo(CovaMetalEditorial, 2432885520);\n906\tconst CovaThermLightEditorialAdapter = makeContenuSeo(CovaThermLightEditorial, 2142459583);\n907\t\n908\t// ---------------------------------------------------------------------------\n909\t// Autres solutions (grille_solutions)\n910\t// ---------------------------------------------------------------------------\n911\t\n912\tconst makeRelated = (currentProduct: string) =>\n913\t  function RelatedProductsAdapter(s: Section<GrilleSolutionsBlock>) {\n914\t    const slugs = arr(s.produits?.nodes)\n915\t      .map((n) => n.slug)\n916\t      .filter((x): x is string => !!x);\n917\t    const products = slugs\n918\t      .map((slug) => allProducts.find((p) => p.href.endsWith(`/${slug}`)))\n919\t      .filter((p): p is RelatedProductItem => !!p);\n920\t    return (\n921\t      <RelatedProducts\n922\t        currentProduct={currentProduct}\n923\t        badge={s.entete?.badge ?? undefined}\n924\t        titre={s.entete?.titre ?? undefined}\n925\t        products={products.length ? products : undefined}\n926\t      />\n927\t    );\n928\t  };\n929\t\n930\tconst RelatedProductsCovaTherm = makeRelated(\"CovaTherm\");\n931\tconst RelatedProductsCovaThermLight = makeRelated(\"CovaTherm Light\");\n932\tconst RelatedProductsCovaSeal = makeRelated(\"CovaSeal 20\");\n933\tconst RelatedProductsCovaMetal = makeRelated(\"CovaMetal 20\");\n934\t\n935\t// ---------------------------------------------------------------------------\n936\t// Showcases dédiés (texte_image \"Positionnement\")\n937\t// ---------------------------------------------------------------------------\n938\t\n939\tconst makeShowcase = (\n940\t  Comp: ComponentType<{\n941\t    badge?: string;\n942\t    titre?: ReactNode;\n943\t    contenu?: string;\n944\t    image?: string;\n945\t    imageAlt?: string;\n946\t    ctaLabel?: string;\n947\t    ctaHref?: string;\n948\t    note?: ReactNode;\n949\t  }>,\n950\t  contenuDefault: string,\n951\t) =>\n952\t  function ShowcaseAdapter(s: Section<TexteImageBlock>) {\n953\t    const img = mapImage(s.image);\n954\t    return (\n955\t      <Comp\n956\t        badge={s.entete?.badge ?? undefined}\n957\t        titre={accentNode(s.entete?.titre, \"text-teal-vivid\")}\n958\t        contenu={richOr(s.contenu, contenuDefault)}\n959\t        image={img?.sourceUrl}\n960\t        imageAlt={img?.altText || undefined}\n961\t        ctaLabel={s.cta?.label ?? undefined}\n962\t        ctaHref={s.cta?.lien ?? undefined}\n963\t        note={s.note ?? undefined}\n964\t      />\n965\t    );\n966\t  };\n967\t\n968\tconst CovaSealShowcaseAdapter = makeShowcase(\n969\t  CovaSealShowcase,\n970\t  \"Système 2 couches qui combine la performance thermique d'une peinture cool roof et la protection d'une étanchéité polyuréthane acrylique élastomère sur le même chantier. Pensé pour les toitures qui présentent des microfissures, des boursouflures ou de petites infiltrations. Rebouche jusqu'à 2 mm , protège et rafraîchit sans réfection complète. Base Coat — étanchéité liquide cool roof (2 à 3 kg/m²). Top Coat — finition anti-encrassement haute durabilité.\",\n971\t);\n972\tconst CovaMetalShowcaseAdapter = makeShowcase(\n973\t  CovaMetalShowcase,\n974\t  \"Une seule application pour traiter simultanément les trois problèmes récurrents des toitures en bac acier vieillissantes : corrosion, fuites sur chevauchements et surchauffe estivale. Anti-corrosion — stoppe la progression de la rouille sur le métal exposé. Étanchéité — membrane élastique à 200 % d'allongement sur chevauchements et points de fixation. Cool roof — jusqu'à 90 % de rayonnement solaire réfléchi, sur 20 ans.\",\n975\t);\n976\tconst CovaThermLightShowcaseAdapter = makeShowcase(\n977\t  CovaThermLightShowcase,\n978\t  \"Le seul revêtement cool roof conçu pour les surfaces qu'on ne peut pas peindre en blanc opaque sans sacrifier la lumière naturelle. Filtre sélectivement UV et infrarouges, laisse passer le spectre visible. Polycarbonate — lanterneaux, voûtes éclairantes, skydômes modernes. Polyester translucide — voûtes éclairantes et skydômes anciens.\",\n979\t);\n980\t\n981\t// ---------------------------------------------------------------------------\n982\t// Avant / après (avant_apres, CovaTherm Light)\n983\t// ---------------------------------------------------------------------------\n984\t\n985\tfunction CovaThermLightBeforeAfterAdapter(s: Section<AvantApresBlock>) {\n986\t  const avant = mapImage(s.imageAvant);\n987\t  const apres = mapImage(s.imageApres);\n988\t  return (\n989\t    <CovaThermLightBeforeAfter\n990\t      badge={s.entete?.badge ?? undefined}\n991\t      titre={accentNode(s.entete?.titre, \"text-foreground/45\")}\n992\t      intro={s.entete?.intro ?? undefined}\n993\t      imageAvant={avant?.sourceUrl}\n994\t      imageApres={apres?.sourceUrl}\n995\t      altAvant={avant?.altText || undefined}\n996\t      altApres={apres?.altText || undefined}\n997\t      legendeAvant={s.legendeAvant ? s.legendeAvant.replace(/^avant\\.?\\s*/i, \"\") : undefined}\n998\t      legendeApres={s.legendeApres ? s.legendeApres.replace(/^après\\.?\\s*/i, \"\") : undefined}\n999\t    />\n1000\t  );\n1001\t}\n1002\t\n1003\t// ---------------------------------------------------------------------------\n1004\t// Registry\n1005\t// ---------------------------------------------------------------------------\n1006\t\n1007\texport const origineProduits: Record<string, ComponentType<any>> = {\n1008\t  // Héros (un par produit : tagline/accentColor proviennent de la vue d'origine)\n1009\t  ProductHeroCovaTherm,\n1010\t  ProductHeroCovaThermLight,\n1011\t  ProductHeroCovaSeal,\n1012\t  ProductHeroCovaMetal,\n1013\t\n1014\t  // Sections génériques configurées par vue\n1015\t  ProductShowcase: ProductShowcaseAdapter,\n1016\t  CompatibilityTabsCovaTherm,\n1017\t  CompatibilityTabsCovaThermLight,\n1018\t  CompatibilityTabsCovaSeal,\n1019\t  CompatibilityTabsCovaMetal,\n1020\t  TechnicalSpecs: TechnicalSpecsAdapter,\n1021\t  ProductAdvantages: ProductAdvantagesAdapter,\n1022\t  ProductFAQCovaTherm,\n1023\t  ProductFAQCovaThermLight,\n1024\t  ProductFAQCovaSeal,\n1025\t  ProductFAQCovaMetal,\n1026\t  CoolRoofGuide: CoolRoofGuideAdapter,\n1027\t  RelatedProductsCovaTherm,\n1028\t  RelatedProductsCovaThermLight,\n1029\t  RelatedProductsCovaSeal,\n1030\t  RelatedProductsCovaMetal,\n1031\t\n1032\t  // CovaSeal 20\n1033\t  CovaSealShowcase: CovaSealShowcaseAdapter,\n1034\t  CovaSealSpecs: CovaSealSpecsAdapter,\n1035\t  CovaSealCertifications: CovaSealCertificationsAdapter,\n1036\t  CovaSealAdvantages: CovaSealAdvantagesAdapter,\n1037\t  CovaSealComparison: CovaSealComparisonAdapter,\n1038\t  CovaSealEditorial: CovaSealEditorialAdapter,\n1039\t\n1040\t  // CovaMetal 20\n1041\t  CovaMetalShowcase: CovaMetalShowcaseAdapter,\n1042\t  CovaMetalSpecs: CovaMetalSpecsAdapter,\n1043\t  CovaMetalCertifications: CovaMetalCertificationsAdapter,\n1044\t  CovaMetalAdvantages: CovaMetalAdvantagesAdapter,\n1045\t  CovaMetalComparison: CovaMetalComparisonAdapter,\n1046\t  CovaMetalEditorial: CovaMetalEditorialAdapter,\n1047\t\n1048\t  // CovaTherm Light\n1049\t  CovaThermLightShowcase: CovaThermLightShowcaseAdapter,\n1050\t  CovaThermLightBeforeAfter: CovaThermLightBeforeAfterAdapter,\n1051\t  CovaThermLightSpecs: CovaThermLightSpecsAdapter,\n1052\t  CovaThermLightCertifications: CovaThermLightCertificationsAdapter,\n1053\t  CovaThermLightAdvantages: CovaThermLightAdvantagesAdapter,\n1054\t  CovaThermLightComparison: CovaThermLightComparisonAdapter,\n1055\t  CovaThermLightEditorial: CovaThermLightEditorialAdapter,\n1056\t};\n1057\t"}]},"uuid":"5885abb1-0b6b-4985-b912-c61fbd2314e6","timestamp":"2026-06-14T16:48:22.128Z","sourceToolAssistantUUID":"4d71b52a-2613-41aa-a3cb-91d148216a74","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"5885abb1-0b6b-4985-b912-c61fbd2314e6","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_01QojMoR9N147XfPQHmA34dn","type":"message","role":"assistant","content":[{"type":"text","text":"Perfect. Now let me check a few more key pieces to complete the picture:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":4,"cache_creation_input_tokens":24926,"cache_read_input_tokens":35760,"cache_creation":{"ephemeral_5m_input_tokens":24926,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"diagnostics":null},"requestId":"req_011Cc3VcCrbEFF6o4sJNVzjb","attributionAgent":"Explore","type":"assistant","uuid":"aefb6e3c-f3d1-4e6a-909e-90ef8f6d0e2d","timestamp":"2026-06-14T16:48:27.101Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"aefb6e3c-f3d1-4e6a-909e-90ef8f6d0e2d","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_01QojMoR9N147XfPQHmA34dn","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01Np9hLj4nGfmJQyRkRoiq2Y","name":"Read","input":{"file_path":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/composantReact.ts"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":4,"cache_creation_input_tokens":24926,"cache_read_input_tokens":35760,"cache_creation":{"ephemeral_5m_input_tokens":24926,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"diagnostics":null},"requestId":"req_011Cc3VcCrbEFF6o4sJNVzjb","attributionAgent":"Explore","type":"assistant","uuid":"ee5e185f-9308-4716-9d47-a36287b4e997","timestamp":"2026-06-14T16:48:28.106Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"ee5e185f-9308-4716-9d47-a36287b4e997","isSidechain":true,"promptId":"7f3e3cb7-906e-4e3a-92fd-290df6bb8908","agentId":"a80d1ba9a1945c3c3","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01Np9hLj4nGfmJQyRkRoiq2Y","type":"tool_result","content":"1\timport { SECTIONS_TYPE_PREFIX } from \"./prefix\";\n2\t\n3\texport const COMPOSANT_REACT_FRAGMENT = /* GraphQL */ `\n4\t  fragment ComposantReactFields on ${SECTIONS_TYPE_PREFIX}ComposantReactLayout {\n5\t    origine\n6\t    __typename\n7\t    composant\n8\t    titre\n9\t    hubspotFormId\n10\t  }\n11\t`;\n12\t"}]},"uuid":"64050dbb-153b-4bff-8878-8e37118fa2c6","timestamp":"2026-06-14T16:48:28.141Z","sourceToolAssistantUUID":"ee5e185f-9308-4716-9d47-a36287b4e997","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"64050dbb-153b-4bff-8878-8e37118fa2c6","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_01QojMoR9N147XfPQHmA34dn","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01YSSjj3oBHgz9tt5RhS8xcJ","name":"Read","input":{"file_path":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/types.ts","limit":100},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":4,"cache_creation_input_tokens":24926,"cache_read_input_tokens":35760,"cache_creation":{"ephemeral_5m_input_tokens":24926,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"diagnostics":null},"requestId":"req_011Cc3VcCrbEFF6o4sJNVzjb","attributionAgent":"Explore","type":"assistant","uuid":"7ce225a9-41ee-4c0c-938e-7191128e5e93","timestamp":"2026-06-14T16:48:33.183Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"7ce225a9-41ee-4c0c-938e-7191128e5e93","isSidechain":true,"promptId":"7f3e3cb7-906e-4e3a-92fd-290df6bb8908","agentId":"a80d1ba9a1945c3c3","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01YSSjj3oBHgz9tt5RhS8xcJ","type":"tool_result","content":"1\t// Types TS du contrat de schéma WordPress (wordpress/SCHEMA.md).\n2\t// Les repeaters/flexibles vides sortent `null` en GraphQL → tout tableau est nullable.\n3\t// Le nom complet des types de layouts dépend du préfixe généré par WPGraphQL for ACF :\n4\t// `__typename` est typé `string` et le BlockRenderer matche par suffixe `...XxxLayout`.\n5\t\n6\t// ---------------------------------------------------------------------------\n7\t// Briques de base\n8\t// ---------------------------------------------------------------------------\n9\t\n10\t/** Image aplatie, prête pour next/image (sortie de mapImage). */\n11\texport interface WpImage {\n12\t  sourceUrl: string;\n13\t  altText: string;\n14\t  width: number | null;\n15\t  height: number | null;\n16\t}\n17\t\n18\t/** Champ image brut tel que renvoyé par WPGraphQL (connection edge). */\n19\texport interface WpImageField {\n20\t  node?: {\n21\t    sourceUrl?: string | null;\n22\t    altText?: string | null;\n23\t    mediaDetails?: { width?: number | null; height?: number | null } | null;\n24\t  } | null;\n25\t}\n26\t\n27\t/** Champ fichier brut (PDF, etc.). */\n28\texport interface WpFileField {\n29\t  node?: {\n30\t    mediaItemUrl?: string | null;\n31\t    title?: string | null;\n32\t  } | null;\n33\t}\n34\t\n35\t/** Clone `clone_cta`. */\n36\texport interface WpCta {\n37\t  label?: string | null;\n38\t  lien?: string | null;\n39\t  style?: \"primary\" | \"secondary\" | \"ghost\" | string | null;\n40\t}\n41\t\n42\t/** Clone `clone_entete_section`. */\n43\texport interface WpEntete {\n44\t  badge?: string | null;\n45\t  titre?: string | null;\n46\t  intro?: string | null;\n47\t}\n48\t\n49\t/** Groupe `clone_seo`, exposé sous `seo`. */\n50\texport interface WpSeo {\n51\t  titreSeo?: string | null;\n52\t  metaDescription?: string | null;\n53\t  canonical?: string | null;\n54\t  ogImage?: WpImageField | null;\n55\t  noindex?: boolean | null;\n56\t}\n57\t\n58\t/** Node minimal d'une relationship (Produit / Industrie / Référence). */\n59\texport interface WpRelNode {\n60\t  id: string;\n61\t  slug?: string | null;\n62\t  title?: string | null;\n63\t}\n64\t\n65\texport interface WpRelProduitNode extends WpRelNode {\n66\t  ficheProduit?: WpFicheProduit | null;\n67\t}\n68\t\n69\texport interface WpRelConnection<TNode = WpRelNode> {\n70\t  nodes?: TNode[] | null;\n71\t}\n72\t\n73\t/** Terme de taxonomie (secteur, type_support). */\n74\texport interface WpTermNode {\n75\t  id: string;\n76\t  slug?: string | null;\n77\t  name?: string | null;\n78\t}\n79\t\n80\texport interface WpTermConnection {\n81\t  nodes?: WpTermNode[] | null;\n82\t}\n83\t\n84\t// ---------------------------------------------------------------------------\n85\t// Layouts génériques (21)\n86\t// ---------------------------------------------------------------------------\n87\t\n88\texport interface HeroBlock {\n89\t  __typename: string;\n90\t  origine?: string | null;\n91\t  variante?: \"home\" | \"standard\" | \"compact\" | string | null;\n92\t  eyebrow?: string | null;\n93\t  titre?: string | null;\n94\t  lead?: string | null;\n95\t  image?: WpImageField | null;\n96\t  stats?: { value?: string | null; label?: string | null }[] | null;\n97\t  badges?: { texte?: string | null }[] | null;\n98\t  ctaPrimaire?: WpCta | null;\n99\t  ctaSecondaire?: WpCta | null;\n100\t}"}]},"uuid":"2ea2cb52-2b25-496d-b21e-283cd74cbee9","timestamp":"2026-06-14T16:48:33.228Z","sourceToolAssistantUUID":"7ce225a9-41ee-4c0c-938e-7191128e5e93","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"2ea2cb52-2b25-496d-b21e-283cd74cbee9","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_01QojMoR9N147XfPQHmA34dn","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01HAUnRfv31H32Nf6A2t9rJo","name":"Bash","input":{"command":"find /opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components -type f -name \"ProductFAQ*.tsx\" -o -name \"CovaThermLight*.tsx\" | head -3"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":4,"cache_creation_input_tokens":24926,"cache_read_input_tokens":35760,"cache_creation":{"ephemeral_5m_input_tokens":24926,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"diagnostics":null},"requestId":"req_011Cc3VcCrbEFF6o4sJNVzjb","attributionAgent":"Explore","type":"assistant","uuid":"812c231f-8682-4f4f-85f7-64bc269d0116","timestamp":"2026-06-14T16:48:33.412Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"812c231f-8682-4f4f-85f7-64bc269d0116","isSidechain":true,"promptId":"7f3e3cb7-906e-4e3a-92fd-290df6bb8908","agentId":"a80d1ba9a1945c3c3","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01HAUnRfv31H32Nf6A2t9rJo","type":"tool_result","content":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/product/ProductFAQ.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/product/covatherm-light/CovaThermLightEditorial.tsx\n/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/product/covatherm-light/CovaThermLightSpecs.tsx","is_error":false}]},"uuid":"7a0762a5-7e85-4d55-aaec-306db1b1701d","timestamp":"2026-06-14T16:48:33.513Z","sourceToolAssistantUUID":"812c231f-8682-4f4f-85f7-64bc269d0116","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"7a0762a5-7e85-4d55-aaec-306db1b1701d","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_013TxCFLqzrEUtG88osNpg2r","type":"message","role":"assistant","content":[{"type":"text","text":"Let me check one of the actual original components:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":4,"cache_creation_input_tokens":1744,"cache_read_input_tokens":60686,"cache_creation":{"ephemeral_5m_input_tokens":1744,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"diagnostics":null},"requestId":"req_011Cc3Vd42YfnjQh3ZcedxMp","attributionAgent":"Explore","type":"assistant","uuid":"ffec21b4-2120-45e1-ac8d-c49f720a5c6c","timestamp":"2026-06-14T16:48:36.148Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"ffec21b4-2120-45e1-ac8d-c49f720a5c6c","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_013TxCFLqzrEUtG88osNpg2r","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01YbtkQagWSAxAufwaYQnVu8","name":"Read","input":{"file_path":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/product/ProductFAQ.tsx","limit":60},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":4,"cache_creation_input_tokens":1744,"cache_read_input_tokens":60686,"cache_creation":{"ephemeral_5m_input_tokens":1744,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"diagnostics":null},"requestId":"req_011Cc3Vd42YfnjQh3ZcedxMp","attributionAgent":"Explore","type":"assistant","uuid":"cc641a25-6ba3-4193-a4f0-21baf3f65bdb","timestamp":"2026-06-14T16:48:36.367Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"cc641a25-6ba3-4193-a4f0-21baf3f65bdb","isSidechain":true,"promptId":"7f3e3cb7-906e-4e3a-92fd-290df6bb8908","agentId":"a80d1ba9a1945c3c3","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01YbtkQagWSAxAufwaYQnVu8","type":"tool_result","content":"1\timport type { ReactNode } from 'react';\n2\timport { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion';\n3\timport { ArrowRight } from 'lucide-react';\n4\timport ScrollReveal from '@/components/ScrollReveal';\n5\t\n6\tinterface FAQItem {\n7\t  q: string;\n8\t  a: ReactNode;\n9\t}\n10\t\n11\tinterface ProductFAQProps {\n12\t  productName: string;\n13\t  faqs: FAQItem[];\n14\t  badge?: string;\n15\t  titre?: ReactNode;\n16\t  intro?: ReactNode;\n17\t  ctaLabel?: string;\n18\t  ctaHref?: string;\n19\t}\n20\t\n21\tconst ProductFAQ = ({\n22\t  productName,\n23\t  faqs,\n24\t  badge = 'FAQ',\n25\t  titre,\n26\t  intro,\n27\t  ctaLabel = 'Devis gratuit',\n28\t  ctaHref = '/diagnostic',\n29\t}: ProductFAQProps) => (\n30\t  <section className=\"py-16 lg:py-24 bg-cream\">\n31\t    <div className=\"container mx-auto px-4 lg:px-8\">\n32\t      <ScrollReveal>\n33\t        <div className=\"grid lg:grid-cols-5 gap-12 lg:gap-16\">\n34\t          <div className=\"lg:col-span-2 lg:sticky lg:top-24 lg:self-start\">\n35\t            <p className=\"text-[10px] uppercase tracking-[0.25em] font-semibold text-primary/65 font-body mb-6\">\n36\t              {badge}\n37\t            </p>\n38\t            <h2\n39\t              className=\"font-satoshi text-3xl sm:text-4xl font-black text-foreground !leading-[1.05] mb-3\"\n40\t              style={{ letterSpacing: '-0.03em' }}\n41\t            >\n42\t              {titre ?? (<>Vos questions sur {productName}</>)}\n43\t            </h2>\n44\t            <p className=\"text-foreground/70 text-base font-body leading-relaxed\">\n45\t              {intro ?? \"Une question qui n'est pas ici ? Appelez-nous ou demandez un devis.\"}\n46\t            </p>\n47\t            <div className=\"mt-6\">\n48\t              <a\n49\t                href={ctaHref}\n50\t                className=\"inline-flex items-center gap-1.5 text-sm font-semibold text-primary hover:gap-2.5 transition-all\"\n51\t              >\n52\t                {ctaLabel} <ArrowRight className=\"w-3.5 h-3.5\" />\n53\t              </a>\n54\t            </div>\n55\t          </div>\n56\t\n57\t          <div className=\"lg:col-span-3\">\n58\t            <Accordion type=\"single\" collapsible className=\"space-y-3\">\n59\t              {faqs.map((faq, i) => (\n60\t                <AccordionItem"}]},"uuid":"91713af4-d72f-4421-8ea4-bbd597bcb65e","timestamp":"2026-06-14T16:48:36.397Z","sourceToolAssistantUUID":"cc641a25-6ba3-4193-a4f0-21baf3f65bdb","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"91713af4-d72f-4421-8ea4-bbd597bcb65e","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_013TxCFLqzrEUtG88osNpg2r","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01X49wQXZHBs6sLMjVWe2tUr","name":"Bash","input":{"command":"git -C /opt/projects/.covalba-wp-worktrees/tom-passage-wp log --oneline --all | grep -i \"parite\\|origine\\|registry\" | head -10"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":4,"cache_creation_input_tokens":1744,"cache_read_input_tokens":60686,"output_tokens":194,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":1744},"inference_geo":"not_available","iterations":[{"input_tokens":4,"output_tokens":194,"cache_read_input_tokens":60686,"cache_creation_input_tokens":1744,"cache_creation":{"ephemeral_5m_input_tokens":1744,"ephemeral_1h_input_tokens":0},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011Cc3Vd42YfnjQh3ZcedxMp","attributionAgent":"Explore","type":"assistant","uuid":"cdebde48-d907-41bb-9ae0-43c8d884faa9","timestamp":"2026-06-14T16:48:36.835Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"cdebde48-d907-41bb-9ae0-43c8d884faa9","isSidechain":true,"promptId":"7f3e3cb7-906e-4e3a-92fd-290df6bb8908","agentId":"a80d1ba9a1945c3c3","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01X49wQXZHBs6sLMjVWe2tUr","type":"tool_result","content":"1a21e9f feat(parite): pixel-perfect parity with the original Vite version, WP-fed\n60fc4f1 feat(parite): original components parametrized + origine registry","is_error":false}]},"uuid":"9af2a13c-1437-4ff7-896a-9554991e0cb3","timestamp":"2026-06-14T16:48:36.896Z","sourceToolAssistantUUID":"cdebde48-d907-41bb-9ae0-43c8d884faa9","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"9af2a13c-1437-4ff7-896a-9554991e0cb3","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_017GjTBvBjMf5DqJrVzRxzTX","type":"message","role":"assistant","content":[{"type":"text","text":"Excellent! Let me check those commits for more context:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":4,"cache_creation_input_tokens":1104,"cache_read_input_tokens":62430,"cache_creation":{"ephemeral_5m_input_tokens":1104,"ephemeral_1h_input_tokens":0},"output_tokens":4,"service_tier":"standard","inference_geo":"not_available"},"diagnostics":{"cache_miss_reason":{"type":"unavailable"}}},"requestId":"req_011Cc3VdHjJyMjZh7HNeUj2V","attributionAgent":"Explore","type":"assistant","uuid":"50c77431-7643-4e7e-bd87-38a1e713d68c","timestamp":"2026-06-14T16:48:40.624Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"50c77431-7643-4e7e-bd87-38a1e713d68c","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_017GjTBvBjMf5DqJrVzRxzTX","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01T5fdGhNGQBG9yb6uM3pHBz","name":"Bash","input":{"command":"git -C /opt/projects/.covalba-wp-worktrees/tom-passage-wp show 60fc4f1 --stat | head -40"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":4,"cache_creation_input_tokens":1104,"cache_read_input_tokens":62430,"output_tokens":101,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":1104},"inference_geo":"not_available","iterations":[{"input_tokens":4,"output_tokens":101,"cache_read_input_tokens":62430,"cache_creation_input_tokens":1104,"cache_creation":{"ephemeral_5m_input_tokens":1104,"ephemeral_1h_input_tokens":0},"type":"message"}],"speed":"standard"},"diagnostics":{"cache_miss_reason":{"type":"unavailable"}}},"requestId":"req_011Cc3VdHjJyMjZh7HNeUj2V","attributionAgent":"Explore","type":"assistant","uuid":"ac3f1fab-9a2b-496e-b85b-e29b9937ae95","timestamp":"2026-06-14T16:48:40.767Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"ac3f1fab-9a2b-496e-b85b-e29b9937ae95","isSidechain":true,"promptId":"7f3e3cb7-906e-4e3a-92fd-290df6bb8908","agentId":"a80d1ba9a1945c3c3","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01T5fdGhNGQBG9yb6uM3pHBz","type":"tool_result","content":"commit 60fc4f1094a37ffa591552d605e479dd24252dcc\nAuthor: Tom Assous <tom@paf-studio.dev>\nDate:   Fri Jun 12 16:35:26 2026 +0000\n\n    feat(parite): original components parametrized + origine registry\n    \n    User requirement: WP-driven pages must render THE SAME CODE as the pre-CMS\n    version, pixel perfect — not re-implementations.\n    \n    - 107 files: original section components/views parametrized (hard-coded\n      literals -> optional props with byte-identical defaults; JSX untouched,\n      enforced by adversarial git-diff audit, 3 blockers fixed)\n    - origine registry: each WP section row names its original component; the\n      BlockRenderer renders the original via a thin adapter (generic blocks\n      remain only as fallback for BO-added rows without origine)\n    - inverse mappers: WP -> RoofPageData / CustomerReference / SectorConfig /\n      one-off view props (deep-merge with coded data, WP wins field by field)\n    - _absorbe sentinel for rows consumed by a neighboring 1-for-N component\n    - origine field added to all 26 ACF layouts + fragments + types\n    \n    Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>\n\n app/references/[slug]/page.tsx                     |   25 +-\n app/toitures/[slug]/page.tsx                       |   39 +-\n scripts/pixel-diff/capture.mjs                     |   66 ++\n scripts/pixel-diff/compare.py                      |   43 +\n src/components/ApplicatorsSection.tsx              |   24 +-\n src/components/BlogPreview.tsx                     |   20 +-\n src/components/CTASection.tsx                      |   16 +-\n src/components/ConstatSection.tsx                  |   28 +-\n src/components/CoolRoofExplainerSection.tsx        |   36 +-\n src/components/ExpertiseSection.tsx                |   26 +-\n src/components/FAQ.tsx                             |   30 +-\n src/components/Hero.tsx                            |   24 +-\n src/components/PressSection.tsx                    |   14 +-\n src/components/PriceComparisonSection.tsx          |   52 +-\n src/components/PriceVSSection.tsx                  |   97 +-\n src/components/ProcessSection.tsx                  |   41 +-\n src/components/RSESection.tsx                      |   30 +-\n src/components/SEOContent.tsx                      |   19 +-","is_error":false}]},"uuid":"70487e6b-9d6d-4ddc-b8ec-fc6f8e670ddb","timestamp":"2026-06-14T16:48:40.843Z","sourceToolAssistantUUID":"ac3f1fab-9a2b-496e-b85b-e29b9937ae95","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"70487e6b-9d6d-4ddc-b8ec-fc6f8e670ddb","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_014VPKZ3quPBwL5ffcATL4Yn","type":"message","role":"assistant","content":[{"type":"text","text":"Perfect! Now let me read the SCHEMA.md to understand the architecture better:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":797,"cache_read_input_tokens":63534,"cache_creation":{"ephemeral_5m_input_tokens":797,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"diagnostics":null},"requestId":"req_011Cc3VdaQASt5jjQm4bxF2x","attributionAgent":"Explore","type":"assistant","uuid":"6560e5e5-2e50-41ef-a396-c3d1334033e4","timestamp":"2026-06-14T16:48:42.526Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"6560e5e5-2e50-41ef-a396-c3d1334033e4","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_014VPKZ3quPBwL5ffcATL4Yn","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01CjR4X1qmvpvkhzaUL6yDr4","name":"Bash","input":{"command":"find /opt/projects/.covalba-wp-worktrees/tom-passage-wp -name \"SCHEMA.md\" -o -name \"SCHEMA.txt\" | head -5"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":797,"cache_read_input_tokens":63534,"output_tokens":112,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":797},"inference_geo":"not_available","iterations":[{"input_tokens":3,"output_tokens":112,"cache_read_input_tokens":63534,"cache_creation_input_tokens":797,"cache_creation":{"ephemeral_5m_input_tokens":797,"ephemeral_1h_input_tokens":0},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011Cc3VdaQASt5jjQm4bxF2x","attributionAgent":"Explore","type":"assistant","uuid":"dd4b7012-f402-43f1-b196-49f5c161545e","timestamp":"2026-06-14T16:48:42.672Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"dd4b7012-f402-43f1-b196-49f5c161545e","isSidechain":true,"promptId":"7f3e3cb7-906e-4e3a-92fd-290df6bb8908","agentId":"a80d1ba9a1945c3c3","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01CjR4X1qmvpvkhzaUL6yDr4","type":"tool_result","content":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/SCHEMA.md","is_error":false}]},"uuid":"25ec239d-0302-4b8c-8bd4-f4bfac88ad0c","timestamp":"2026-06-14T16:48:42.775Z","sourceToolAssistantUUID":"dd4b7012-f402-43f1-b196-49f5c161545e","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"25ec239d-0302-4b8c-8bd4-f4bfac88ad0c","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_016ZKMNR3rFUNEQkGCdExcxT","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_015aE6fEBFGPA8uGUaXxskAd","name":"Read","input":{"file_path":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/SCHEMA.md","limit":150},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":150,"cache_read_input_tokens":64331,"output_tokens":96,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":150},"inference_geo":"not_available","iterations":[{"input_tokens":3,"output_tokens":96,"cache_read_input_tokens":64331,"cache_creation_input_tokens":150,"cache_creation":{"ephemeral_5m_input_tokens":150,"ephemeral_1h_input_tokens":0},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011Cc3VdieWBJ82DGxxF6dvN","attributionAgent":"Explore","type":"assistant","uuid":"bbba013b-cda4-4322-9937-583831350cf3","timestamp":"2026-06-14T16:48:44.421Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"bbba013b-cda4-4322-9937-583831350cf3","isSidechain":true,"promptId":"7f3e3cb7-906e-4e3a-92fd-290df6bb8908","agentId":"a80d1ba9a1945c3c3","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_015aE6fEBFGPA8uGUaXxskAd","type":"tool_result","content":"1\t# Contrat de schéma — Covalba WordPress headless\n2\t\n3\tSource de vérité du modèle de contenu. Toute évolution passe par une PR qui met à jour\n4\t**en même temps** : ce fichier, le mu-plugin `covalba-core` (PHP) et `src/lib/wp/` +\n5\t`src/components/blocks/` (Next). **On ne renomme jamais un `acf_fc_layout` publié.**\n6\t\n7\t## Conventions\n8\t\n9\t- Noms de champs ACF : `snake_case` français. `graphql_field_name` : `camelCase`.\n10\t- Field keys déterministes : `field_cvb_{layout}_{champ}` (sous-champs : `field_cvb_{layout}_{champ}_{sous_champ}`).\n11\t- Le flexible content unique s'appelle `sections` (key `field_cvb_sections`), field group\n12\t  « Sections de page » (`group_cvb_sections`), `graphql_field_name: sections`,\n13\t  localisé sur : `page`, `produit`, `toiture`, `industrie`, `reference`.\n14\t- WPGraphQL for ACF v2 expose chaque layout en type GraphQL dont le nom **se termine par**\n15\t  `{LayoutPascal}Layout` (ex. layout `grille_cards` → type `...GrilleCardsLayout`).\n16\t  Le BlockRenderer matche par suffixe, jamais par nom complet.\n17\t- Tout champ image : `return_format = array` côté ACF ; côté GraphQL on lit\n18\t  `image { node { sourceUrl altText mediaDetails { width height } } }`.\n19\t- Les repeaters/flexibles vides sortent `null` en GraphQL → toujours mapper `?? []`.\n20\t- Titres : la convention `*texte*` marque le span d'accent (rendu `<span class=\"accent\">` côté front).\n21\t\n22\t## CPT\n23\t\n24\t| CPT | slug | rewrite | graphql single/plural | supports | menu_icon |\n25\t|---|---|---|---|---|---|\n26\t| Produit | `produit` | `solutions` | `produit` / `produits` | title, thumbnail, revisions | dashicons-admin-home |\n27\t| Toiture | `toiture` | `toitures` | `toiture` / `toitures` | title, thumbnail, revisions | dashicons-admin-multisite |\n28\t| Industrie | `industrie` | `industries` | `industrie` / `industries` | title, thumbnail, revisions | dashicons-building |\n29\t| Référence | `reference` | `references` | `reference` / `references` | title, thumbnail, revisions | dashicons-awards |\n30\t\n31\tTous : `public => true`, `publicly_queryable => false` (pas de rendu WP), `show_in_rest => true`,\n32\t`show_in_graphql => true`, `has_archive => false`, `rewrite => ['with_front' => false]`.\n33\tLes one-off (home, contact, qui-sommes-nous, devenir-applicateur, bat-en-112, guide-cool-roof,\n34\tsimulateur-economie-energie, faq, diagnostic, estimation, hub industrie, listing references) = **pages natives WP**.\n35\t\n36\t## Taxonomies\n37\t\n38\t| Taxonomie | slug | graphql | attachée à | termes initiaux |\n39\t|---|---|---|---|---|\n40\t| Secteur | `secteur` | `secteur` / `secteurs` | reference, industrie | industrie, logistique, distribution, tertiaire, collectivites, agricole, erp |\n41\t| Type de support | `type_support` | `typeSupport` / `typesSupport` | reference, produit, toiture | bitume, bac-acier, fibrociment, tuiles-ciment, toiture-plate, epdm, pvc, zinc |\n42\t\n43\t## Clones (sous-structures partagées)\n44\t\n45\t### `clone_entete_section` (group `group_cvb_clone_entete`)\n46\t| champ | type | graphql |\n47\t|---|---|---|\n48\t| badge | text | badge |\n49\t| titre | text (`*accent*` autorisé) | titre |\n50\t| intro | textarea | intro |\n51\t\n52\t### `clone_cta` (group `group_cvb_clone_cta`)\n53\t| champ | type | graphql |\n54\t|---|---|---|\n55\t| label | text | label |\n56\t| lien | text (url ou path interne) | lien |\n57\t| style | select: primary / secondary / ghost | style |\n58\t\n59\t### `clone_seo` (group `group_cvb_seo`, field group séparé « 🔍 SEO », position high, sur tous les types + pages)\n60\t| champ | type | graphql |\n61\t|---|---|---|\n62\t| titre_seo | text | titreSeo |\n63\t| meta_description | textarea rows 3 | metaDescription |\n64\t| canonical | url | canonical |\n65\t| og_image | image | ogImage |\n66\t| noindex | true_false | noindex |\n67\t\n68\tGroup exposé en GraphQL sous `seo`.\n69\t\n70\t## Layouts génériques (disponibles sur tous les types) — 21\n71\t\n72\tChaque layout a `entete` (clone `clone_entete_section`, graphql `entete`) sauf mention contraire.\n73\t\n74\t1. **hero** — variante (select: home/standard/compact), eyebrow (text), titre (text), lead (textarea), image (image), stats (repeater max 4: value text, label text), badges (repeater: texte text), cta_primaire (clone_cta, graphql ctaPrimaire), cta_secondaire (clone_cta, graphql ctaSecondaire). Pas d'entete.\n75\t2. **barre_reassurance** — items (repeater max 6: texte text). Pas d'entete.\n76\t3. **grille_cards** — entete, cards (repeater: icone (select, liste blanche lucide), titre text, texte textarea, image image, lien text), colonnes (select: 2/3/4), theme (select: clair/sombre/accent), transition (textarea).\n77\t4. **texte_image** — entete, contenu (wysiwyg), image (image), position_image (select: gauche/droite, graphql positionImage), liste (repeater: texte text), note (textarea), cta (clone_cta).\n78\t5. **chiffres** — entete, figures (repeater max 6: value text, label text, sublabel text), theme (select: clair/sombre).\n79\t6. **tableau_comparatif** — entete, entetes_colonnes (repeater: texte text, graphql entetesColonnes), lignes_comparatif (repeater, graphql lignesComparatif: cellules (repeater: texte text) — conventions `oui`/`non`/`-`), colonne_mise_en_avant (number, graphql colonneMiseEnAvant), texte_apres (textarea, graphql texteApres), cta (clone_cta).\n80\t7. **tableau_situations** — entete, lignes_situations (repeater, graphql lignesSituations: situation text, recommandation text, justification textarea), cta (clone_cta).\n81\t8. **etapes** — entete, etapes (repeater: titre text, texte textarea, image image), reassurance (textarea), video_url (url, graphql videoUrl).\n82\t9. **faq** — entete, questions (repeater: question text, reponse wysiwyg), json_ld (true_false défaut true, graphql jsonLd), cta (clone_cta).\n83\t10. **cta** — variante (select: mid/final/banner), titre (text), texte (textarea), reassurances (repeater: texte text), cta_primaire (clone_cta, graphql ctaPrimaire), cta_secondaire (clone_cta, graphql ctaSecondaire), hubspot_form_id (text, graphql hubspotFormId). Pas d'entete.\n84\t11. **temoignages_video** — entete, temoignages (repeater: video_url url (graphql videoUrl), citation textarea, nom text, role text, entreprise text, contexte textarea).\n85\t12. **video** — entete, video_url (url, graphql videoUrl), points_cles (repeater: label text, texte textarea — graphql pointsCles).\n86\t13. **logos** — entete, source (select: clients/presse/custom), logos_custom (repeater: image image, nom text, lien text — graphql logosCustom).\n87\t14. **citation** — texte (textarea), auteur (text), role (text), photo (image). Pas d'entete.\n88\t15. **contenu_seo** — intro (wysiwyg), sections (repeater: titre text, contenu wysiwyg), sources (repeater: label text, url url), replie (true_false défaut true). Pas d'entete (le H2 vit dans sections).\n89\t16. **compatibilite_supports** — entete, supports (repeater: type text, detail textarea, image image), note_couverture (textarea, graphql noteCouverture), rendu (select: tabs/grille).\n90\t17. **grille_secteurs** — entete, mode (select: auto/manuel), secteurs (relationship → industrie, graphql secteurs).\n91\t18. **grille_solutions** — entete, produits (relationship → produit), affichage (select: cards/compact).\n92\t19. **references_grille** — entete, mode (select: toutes/secteur/manuel), secteur (taxonomy term select secteur), limite (number), references_manuel (relationship → reference, graphql referencesManuel), filtres (true_false, graphql filtres).\n93\t20. **timeline** — entete, jalons (repeater: annee text, titre text, texte textarea, image image).\n94\t21. **composant_react** — composant (select: simulateur_energie / diagnostic_toiture / estimation / climate_dashboard / applicateur_screener / formulaire_hubspot / carte_intervention), titre (text), hubspot_form_id (text, graphql hubspotFormId). Pas d'entete. **Escape hatch** : monte un composant interactif codé côté front.\n95\t\n96\t## Layouts spécifiques — 5 (filtrés par type côté BO via acf/load_field, présents dans le même flexible)\n97\t\n98\t22. **specs_techniques** (produit) — entete, colonnes_specs (repeater, graphql colonnesSpecs: nom text), lignes_specs (repeater, graphql lignesSpecs: label text, valeurs (repeater: texte text)), colonne_mise_en_avant (number, graphql colonneMiseEnAvant), fiche_technique (file, graphql ficheTechnique).\n99\t23. **certifications** (produit) — entete, items_certifications (repeater, graphql itemsCertifications: logo image, nom text, description textarea).\n100\t24. **variantes_produit** (produit) — entete, variantes (repeater: nom text, description textarea, garantie text, sri text, usage text).\n101\t25. **avant_apres** (reference, toiture, produit) — entete, image_avant (image, graphql imageAvant), image_apres (image, graphql imageApres), legende_avant (text, graphql legendeAvant), legende_apres (text, graphql legendeApres).\n102\t26. **fiche_chantier** (reference) — afficher (true_false défaut true). Rend les données du groupe `ficheReference` du post courant.\n103\t\n104\t### Filtrage BO (n'affecte pas le schéma GraphQL)\n105\t- `page` : tout sauf specs_techniques, variantes_produit, fiche_chantier\n106\t- `produit` : tout sauf fiche_chantier\n107\t- `toiture` : tout sauf specs_techniques, certifications, variantes_produit, fiche_chantier\n108\t- `industrie` : tout sauf specs_techniques, certifications, variantes_produit, fiche_chantier, avant_apres\n109\t- `reference` : tout sauf specs_techniques, variantes_produit, grille_secteurs\n110\t\n111\t## Groupes de champs structurés (hors flexible)\n112\t\n113\t### `ficheProduit` (group `group_cvb_fiche_produit`, sur produit, graphql `ficheProduit`)\n114\tproduct_name text (productName), tagline text, accent_color text hex (accentColor), garantie text,\n115\tsri text, prix text, fiche_technique_pdf file (ficheTechniquePdf).\n116\t\n117\t### `ficheReference` (group `group_cvb_fiche_reference`, sur reference, graphql `ficheReference`)\n118\tclient_name text (clientName), location text, produit relationship→produit (produit),\n119\tsupport text, surface text, metrics repeater (value text, label text), quote group (texte textarea,\n120\tauteur text, role text), video_url url (videoUrl), show_in_grid true_false (showInGrid).\n121\t\n122\t## Options pages (graphql `optionsSite`, slug admin `options-site`, capability edit_posts)\n123\t\n124\tOnglets (tabs ACF) :\n125\t1. **Navigation** : menu_principal repeater (label text, lien text, sous_items repeater (label, lien, description text)) (menuPrincipal), cta_navbar clone_cta (ctaNavbar).\n126\t2. **Footer** : colonnes_footer repeater (titre text, liens repeater (label, lien)) (colonnesFooter), texte_legal textarea (texteLegal), reseaux_sociaux repeater (reseau select: linkedin/youtube/instagram/x, url url) (reseauxSociaux).\n127\t3. **Coordonnées** : telephone text, email text, adresse textarea, horaires text.\n128\t4. **CTA globaux** : sticky_cta_label text (stickyCtaLabel), sticky_cta_lien text (stickyCtaLien), reassurances_defaut repeater (texte text) (reassurancesDefaut).\n129\t5. **Preuves** : logos_clients repeater (image image, nom text) (logosClients), logos_presse repeater (image image, nom text, lien text) (logosPresse), chiffres_cles repeater (value, label) (chiffresCles).\n130\t6. **SEO défaut** : og_image_defaut image (ogImageDefaut), suffixe_title text (suffixeTitle).\n131\t\n132\t## ⚠ Piège WPGraphQL for ACF : noms de sous-champs partagés\n133\t\n134\tLes types GraphQL des repeaters/groups imbriqués sont nommés `{Prefix}{NomDuChamp}` SANS le\n135\tlayout parent : deux layouts qui partagent un nom de champ imbriqué partagent le MÊME type.\n136\tRègle : un nom de champ imbriqué réutilisé entre layouts doit avoir des sous-champs IDENTIQUES\n137\t(ex. `entete`, `cta`) — sinon suffixer le nom par le layout (`lignes_comparatif`,\n138\t`lignes_situations`, `lignes_specs`, `colonnes_specs`, `items_certifications`).\n139\t\n140\t## Icônes lucide autorisées (select `icone`)\n141\t\n142\tthermometer-sun, thermometer-snowflake, droplets, shield-check, clock, euro, leaf, factory,\n143\twarehouse, store, building-2, school, tractor, sun, snowflake, wrench, hard-hat, check-circle-2,\n144\ttrending-down, trending-up, zap, wind, cloud-sun, gauge, ruler, paint-roller, spray-can, map-pin,\n145\tphone, file-text, award, users\n146\t"}]},"uuid":"f15df9dd-c562-482c-aa6e-f57f3ab0b977","timestamp":"2026-06-14T16:48:44.443Z","sourceToolAssistantUUID":"bbba013b-cda4-4322-9937-583831350cf3","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
{"parentUuid":"f15df9dd-c562-482c-aa6e-f57f3ab0b977","isSidechain":true,"agentId":"a80d1ba9a1945c3c3","message":{"model":"claude-haiku-4-5-20251001","id":"msg_016XqVy87ZhBBFnjH8a83zvM","type":"message","role":"assistant","content":[{"type":"text","text":"Perfect! Now I have all the information I need. Let me create a comprehensive report:\n\n## Comprehensive Report: \"Composant d'origine\" / \"Origine Registry\" Feature\n\nI have fully investigated the origem registry logic in the codebase. Here is the complete explanation:\n\n---\n\n### 1. **The Registry File & Structure**\n\n**Path:** `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/origine/index.ts`\n\n```typescript\n// Registry des composants d'ORIGINE du site, keyé par la valeur du champ\n// `origine` des sections WordPress.\nimport { origineHome } from \"./home\";\nimport { origineProduits } from \"./produits\";\nimport { origineIndustrie } from \"./industrie\";\nimport { origineOneoff } from \"./oneoff\";\n\nexport const origineRegistry: Record<string, ComponentType<any>> = {\n  ...origineHome,\n  ...origineProduits,\n  ...origineIndustrie,\n  ...origineOneoff,\n};\n```\n\nThe registry is a flat **dictionary mapping component names (strings) to React components**. It aggregates four families of adapters, split by page family to allow parallel work:\n- `origineHome`: ~19 adapters for home page sections\n- `origineProduits`: ~40+ adapters for the 4 product pages\n- `origineIndustrie`: industry pages\n- `origineOneoff`: one-off pages (contact, etc.)\n\n---\n\n### 2. **ACF Field Definition & Data Flow**\n\n**Path:** `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/helpers.php` (lines 128–142)\n\nAll 26 flexible content layouts automatically get an `origine` field, added by the `cvb_layout()` helper:\n\n```php\nfunction cvb_layout(string $name, string $label, array $sub_fields): array {\n    // Champ commun `origine` : nom du composant React d'ORIGINE qui rend cette\n    // section (parité pixel avec la version d'avant-CMS). Posé par le seed,\n    // lecture seule côté BO. Vide => rendu générique du layout.\n    $sub_fields[] = [\n        'key'                => \"field_cvb_{$name}_origine\",\n        'name'               => 'origine',\n        'label'              => \"Composant d'origine\",\n        'type'               => 'text',\n        'readonly'           => 1,  // <-- Read-only in backoffice\n        'instructions'       => 'Géré par les développeurs — composant du site qui rend ce block.',\n        'show_in_graphql'    => 1,\n        'graphql_field_name' => 'origine',\n        'wrapper'            => ['width' => '40'],\n    ];\n    return [ ... ];\n}\n```\n\n**Key traits:**\n- **Text field**, read-only in the WordPress backoffice (developers only)\n- **Exposed to GraphQL** as `origine` (camelCase matches)\n- **Every layout includes it** — if empty, generic block fallback kicks in\n- **Instructions:** \"Géré par les développeurs — composant du site qui rend ce block\" (Managed by developers — the component that renders this block)\n\nExample WP section in JSON:\n```json\n{\n  \"__typename\": \"SomeLayout\",\n  \"origine\": \"ProductFAQCovaThermLight\",\n  \"titre\": \"Your questions\",\n  ...other block fields\n}\n```\n\n---\n\n### 3. **Rendering Logic: BlockRenderer**\n\n**Path:** `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/blocks/BlockRenderer.tsx` (lines 77–120)\n\nThe `BlockRenderer` is the resolver. It receives a flat array of WP sections and applies **2-tier routing**:\n\n```typescript\nexport function BlockRenderer({\n  sections,\n}: {\n  sections: WpSection[] | null | undefined;\n}) {\n  if (!sections?.length) return null;\n\n  return (\n    <>\n      {sections.map((section, i) => {\n        const typename = section?.__typename;\n        if (!typename) return null;\n\n        // 1) Composant d'origine — pixel-perfect parity via adapter\n        //    Sentinel \"_absorbe\" : row whose data is consumed by a neighboring\n        //    1-for-N component (no render).\n        const origine = section.origine;\n        if (origine === \"_absorbe\") return null;\n        if (origine && origineRegistry[origine]) {\n          const Original = origineRegistry[origine];\n          return <Original key={i} {...section} position={i} />;\n        }\n        if (origine && process.env.NODE_ENV !== \"production\") {\n          console.warn(\n            `[BlockRenderer] origine \"${origine}\" sans adaptateur — fallback générique`,\n          );\n        }\n\n        // 2) Fallback: generic layout matched on __typename suffix\n        //    (for BO-added rows without origine, or if lookup fails)\n        const key = registryKeys.find((k) => typename.endsWith(k));\n        if (!key) {\n          if (process.env.NODE_ENV !== \"production\") {\n            console.warn(`[BlockRenderer] Layout inconnu : ${typename}`);\n          }\n          return null;\n        }\n\n        const Block = registry[key];\n        return <Block key={i} {...section} position={i} />;\n      })}\n    </>\n  );\n}\n```\n\n**Resolution flow:**\n1. Extract `origine` string from the WP block data\n2. Check if `origine === \"_absorbe\"` (sentinel for 1-for-N components that consume neighboring rows) → skip render\n3. Look up `origineRegistry[origine]` → if found, render the original component\n4. If `origine` is set but not found → warn in dev, fallback\n5. If no `origine` → match by `__typename` suffix against the generic `registry` (fallback layouts like `Hero`, `BarreReassurance`, etc.)\n6. If no match → warn and return null\n\n---\n\n### 4. **Example: Original Component with Adapter**\n\n**Path:** `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/product/ProductFAQ.tsx` (original component, first 30 lines)\n\n```typescript\ninterface ProductFAQProps {\n  productName: string;\n  faqs: FAQItem[];\n  badge?: string;              // <-- Defaults provided\n  titre?: ReactNode;\n  intro?: ReactNode;\n  ctaLabel?: string;\n  ctaHref?: string;\n}\n\nconst ProductFAQ = ({\n  productName,\n  faqs,\n  badge = 'FAQ',             // <-- Byte-identical defaults\n  titre,\n  intro,\n  ctaLabel = 'Devis gratuit',\n  ctaHref = '/diagnostic',\n}: ProductFAQProps) => ( ... )\n```\n\n**Path:** `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/origine/produits.tsx` (adapter, lines 862–865)\n\n```typescript\nconst makeFaq = (productName: string, fallback: ProductFaqEntry[]) =>\n  function FaqAdapter(s: Section<FaqBlock>) {\n    const questions = arr(s.questions);\n    const faqs = questions.length\n      ? questions.map((q) => ({\n          q: q.question ?? \"\",\n          a: (\n            <div\n              className=\"[&_a]:text-primary [&_a]:font-semibold [&_a:hover]:underline\"\n              dangerouslySetInnerHTML={{ __html: q.reponse ?? \"\" }}\n            />\n          ),\n        }))\n      : fallback.map(({ q, text }) => ({ q, a: text as ReactNode }));\n    return (\n      <ProductFAQ\n        productName={productName}\n        faqs={faqs}\n        badge={s.entete?.badge ?? undefined}    // <-- WP field, or undefined\n        titre={s.entete?.titre ?? undefined}\n        intro={s.entete?.intro ?? undefined}\n        ctaLabel={s.cta?.label ?? undefined}\n        ctaHref={s.cta?.lien ?? undefined}\n      />\n    );\n  };\n\nconst ProductFAQCovaThermLight = makeFaq(\"CovaTherm Light\", covathermLightFaqs);\n\nexport const origineProduits: Record<string, ComponentType<any>> = {\n  ProductFAQCovaThermLight,   // <-- String key in registry\n  ...\n};\n```\n\n**Data flow:**\n- WP passes block data (JSON) with `origine: \"ProductFAQCovaThermLight\"`\n- BlockRenderer looks up `origineRegistry[\"ProductFAQCovaThermLight\"]` → finds the adapter function\n- Adapter unpacks WP fields (`s.entete?.badge`, `s.cta?.label`, etc.)\n- If WP field is absent/null → passes `undefined` to the original component\n- Original component applies its **byte-identical defaults** (same JSX, same default values)\n- Result: **pixel-perfect parity** with the pre-CMS version\n\n---\n\n### 5. **Real Example: ProductFAQCovaThermLight**\n\nThe registry entry `ProductFAQCovaThermLight` (from produits.tsx, line 863):\n\n```typescript\nconst ProductFAQCovaThermLight = makeFaq(\"CovaTherm Light\", covathermLightFaqs);\n```\n\n- **Key in registry:** `\"ProductFAQCovaThermLight\"` (component name)\n- **In WordPress:** a section row has `origine: \"ProductFAQCovaThermLight\"`\n- **Adapter logic:**\n  - Unpacks `s.questions` (repeater of Q&A pairs from WP)\n  - If empty → uses `covathermLightFaqs` (seed fallback, identical to the original Vite version)\n  - Maps WP questions into the `ProductFAQ` component\n  - Passes product name `\"CovaTherm Light\"` (hard-coded in the adapter factory)\n- **Rendering:** Original `ProductFAQ` component renders with WP-provided content or defaults\n\n---\n\n### 6. **The \"Why\": Pixel-Perfect Parity Requirement**\n\n**Key commit:** `60fc4f1` — \"feat(parite): original components parametrized + origine registry\"\n\n```\nUser requirement: WP-driven pages must render THE SAME CODE as the pre-CMS\nversion, pixel perfect — not re-implementations.\n\n- 107 files: original section components/views parametrized\n- origine registry: each WP section row names its original component; the\n  BlockRenderer renders the original via a thin adapter\n- Fallback: block générique matché sur le suffixe `...XxxLayout` du\n  `__typename` (sections ajoutées dans le BO sans origine)\n```\n\n**Problem solved:**\n1. **Before:** A section like \"Hero\" could exist in two forms:\n   - Original Vite hero (with specific JSX structure, styling, defaults)\n   - Re-implemented WP block hero (often subtle pixel-perfect mismatches)\n2. **After:** The \"1 component = same code\" rule enforces that WP feeds data into **the actual original component**, never a parallel implementation.\n\n**Mechanism:**\n- Original components are **parametrized**: hard-coded defaults become optional props\n- The JSX code is **unchanged** (enforced by git diff audits)\n- Adapters map WP field names → component props (thin translation layer)\n- If WP field is absent → original prop default applies\n- Result: **identical HTML/CSS/behavior** between pre-CMS and WP-fed versions\n\n**Parity rule from CLAUDE.md:**\n```\n\"1 composant = 1 personne\" : ne jamais modifier un fichier qu'un autre dev a en cours (PR ouverte)\n\"1 component = same code, no reimplementation\" — Exigence Tom\n```\n\n---\n\n### 7. **Sentinel: \"_absorbe\"**\n\nThe special value `\"_absorbe\"` marks rows consumed by a 1-for-N component adapter.\n\n**Example:** A timeline section might consume multiple row data; earlier rows would have `origine: \"_absorbe\"` to prevent double-rendering.\n\n```typescript\nif (origine === \"_absorbe\") return null;\n```\n\n---\n\n### 8. **Summary: Data Flow**\n\n```\nWordPress Block (JSON)\n  ├─ __typename: \"ProductFAQCovaThermLightLayout\"\n  ├─ origine: \"ProductFAQCovaThermLight\"      ← developer-managed, read-only field\n  ├─ entete.titre: \"Vos questions…\"\n  ├─ entete.badge: \"FAQ\"\n  └─ questions: [ { question: \"…\", reponse: \"…\" }, … ]\n        │\n        ▼\nBlockRenderer\n  ├─ Check origine ✓ found in registry\n  ├─ Call origineRegistry[\"ProductFAQCovaThermLight\"]\n  └─ Pass {...section, position: i} as props\n        │\n        ▼\nProductFAQCovaThermLight Adapter\n  ├─ Extract s.entete?.badge, s.cta?.label, etc.\n  ├─ Map WP questions to FAQItem[]\n  ├─ Call original ProductFAQ({ productName, faqs, badge, titre, … })\n  └─ Undefined fields → original defaults apply\n        │\n        ▼\nProductFAQ Component (Original, Parametrized)\n  ├─ Render FAQ accordion with product name\n  ├─ Use passed badge or default \"FAQ\"\n  ├─ Layout, styling, structure: byte-identical to Vite version\n  └─ Emit HTML\n```\n\n---\n\n### 9. **Related Files**\n\n- **GraphQL Fragment:** `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/fragments/composantReact.ts` — includes `origine` in the query\n- **Types:** `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/lib/wp/types.ts` — all `Block` interfaces have `origine?: string | null`\n- **SCHEMA.md:** `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/SCHEMA.md` (lines 1–16) — documents the origin field as part of all 26 layouts\n- **Adapter families:** \n  - `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/origine/home.tsx` (19 adapters)\n  - `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/origine/produits.tsx` (40+ adapters)\n\n---\n\n### **Conclusion**\n\nThe **\"Composant d'origine\" registry** is a thin translation layer that enforces **pixel-perfect parity between the pre-CMS Next.js/Vite site and the WP-fed version**. It solves the core requirement: *the same React component code, with the same JSX and default values, renders identically whether fed by seed data or WordPress*. No reimplementations, no parallel code—just parameterized originals + adapters mapping WP fields to props."}],"stop_reason":"end_turn","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":4261,"cache_read_input_tokens":64481,"output_tokens":3798,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":4261},"inference_geo":"not_available","iterations":[{"input_tokens":3,"output_tokens":3798,"cache_read_input_tokens":64481,"cache_creation_input_tokens":4261,"cache_creation":{"ephemeral_5m_input_tokens":4261,"ephemeral_1h_input_tokens":0},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011Cc3Vdqt5dHrwgcQgBJjRg","attributionAgent":"Explore","type":"assistant","uuid":"dafa87d5-baee-41a9-983a-edee4105fd57","timestamp":"2026-06-14T16:49:22.669Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-wp-worktrees/tom-passage-wp","sessionId":"a7015fc2-9c8c-4823-babb-abfa078d42f2","version":"2.1.177","gitBranch":"tom/passage-wp","slug":"aussi-je-veux-m-assurer-polished-eich"}
