{
  "$schema": "factory-v1",
  "flow_name": "escalado_formatos",
  "flow_version": "v2-2026-05-09",
  "flow_description": "Escalado de formatos: dado un video ganador como input, el sistema busca 5 similares + 1 arriesgado, los compara con el ganador en awareness/jerga/deseo y entrega tarjeta a editor de video.",
  "input": {
    "type": "video_ganador",
    "fields": [
      "video_url",
      "estudio_resumen",
      "awareness",
      "sof",
      "jergas",
      "deseo_principal"
    ]
  },
  "output": {
    "type": "trello_card_editor",
    "fields": [
      "card_id",
      "short_url",
      "videos_attached",
      "brief_speech"
    ]
  },
  "stations": [
    {
      "id": "descargar",
      "label": "DESCARGAR",
      "sublabel": "Bajar video del enlace",
      "order": 1,
      "position": {
        "x": -16,
        "z": -8
      },
      "mat_color": "#3498db",
      "border_color": "#1f6feb",
      "agents": [
        {
          "id": "descargador-video",
          "name": "Descargador",
          "skill": "Descargar video desde URL (yt-dlp para FB/IG, Playwright Chrome CDP para FB Ads Library, MCP browser_*).",
          "backend_skill": "yt-dlp + Playwright fallback (no skill .md específica · script directo Bash invoke)",
          "backend_invocation": "Bash: yt-dlp <url> -o <out_path>; si falla → Playwright connect_over_cdp para descargar manualmente",
          "input_from": "INPUT (Fer pasa la URL del video ganador)",
          "output_to": "analizar.analizador-video",
          "verifier_l1": "verify_video_downloaded.py",
          "supervisor_l2_skill": "Detectar si el video bajado es válido (mp4, ≥10s, sin corrupción)",
          "canonical_agent_name": "descargador-video-escalado",
          "canonical_agent_md": ".claude/agents/escalado_formatos/descargador-video-escalado.md"
        }
      ],
      "supervisor": {
        "id": "supervisor-descargar",
        "name": "Supervisor Descargar",
        "scope": "sub-proceso completo descargar"
      }
    },
    {
      "id": "analizar",
      "label": "ANALIZAR",
      "sublabel": "Cache lookup + visual + transcripción",
      "order": 2,
      "position": {
        "x": -8,
        "z": -10
      },
      "mat_color": "#9b59b6",
      "border_color": "#9b59b6",
      "agents": [
        {
          "id": "analizador-video",
          "name": "Analizador",
          "skill": "PASO 1 (cache lookup): buscar análisis previo del producto en `_productos/<product>/_analysis.json` y `_apify_out/*<product>*` y memorias persistentes. Si existe análisis previo válido (mismo producto, mismo país, <30 días) → reutilizar, saltar a OUTPUT. PASO 2 (full analysis): si NO existe cache → análisis visual frame-by-frame + transcripción Whisper + integración → reporte completo del video → guardar en `_productos/<product>/_analysis.json` para futuros lookups.",
          "backend_skill": "faster-whisper (transcripción) + ffmpeg (frame extraction) + LLM analysis call (Opus)",
          "backend_invocation": "Python script: cache_lookup → faster_whisper.transcribe → ffmpeg frames → Claude API call con frames+transcript → save _analysis.json",
          "input_from": "descargar.descargador-video",
          "output_to": "interpretar.interpretador-video",
          "verifier_l1": "verify_analysis_report.py (tiene secciones: visual + speech + síntesis · y campo cache_hit boolean)",
          "supervisor_l2_skill": "Detectar si el análisis es superficial (≥1500 palabras + ≥10 timestamps clave) · y validar que el cache lookup se ejecutó antes que el análisis pesado",
          "cache_lookup_policy": {
            "enabled": true,
            "lookup_paths": [
              "_productos/<product>/_analysis.json",
              "_productos/<product>/*_analysis*.json",
              "_apify_out/*<product>*.json",
              "memory/feedback_*<product>*.md"
            ],
            "max_age_days": 30,
            "match_fields": [
              "product_name",
              "country",
              "video_url"
            ],
            "cache_hit_savings": "salta el análisis Whisper + frame-by-frame · ~3-5 min ahorrados · ~5000 tokens ahorrados"
          },
          "canonical_agent_name": "analizador-combo-completo",
          "canonical_agent_md": ".claude/agents/escalado_formatos/analizador-combo-completo.md"
        }
      ],
      "supervisor": {
        "id": "supervisor-analizar",
        "name": "Supervisor Analizar",
        "scope": "sub-proceso completo analizar · valida cache lookup antes de análisis pesado"
      }
    },
    {
      "id": "interpretar",
      "label": "INTERPRETAR",
      "sublabel": "Qué buscar (awareness/jerga/deseo)",
      "order": 3,
      "position": {
        "x": 0,
        "z": -8
      },
      "mat_color": "#e67e22",
      "border_color": "#e67e22",
      "agents": [
        {
          "id": "interpretador-video",
          "name": "Intérprete",
          "skill": "Lee el reporte del video ganador → extrae el COMBO COMPLETO de búsqueda: awareness, sofisticación, jergas, deseo principal, AVATAR (edad/género/contexto/profesión), TEMPORALIDAD (urgencia, etapa de vida, estacionalidad), FUERZA DE CAMBIO (qué empuja al avatar a actuar AHORA — frustración acumulada / evento detonante / FOMO / dolor agudo / oportunidad), formato/ángulo, antagonistas explícitos. Si saturated=true, marca need_complex_videos=true.",
          "backend_skill": "Claude Opus inline call (subagent general-purpose con prompt de extracción de combo completo)",
          "backend_invocation": "Task tool con subagent_type=general-purpose · input: _analysis.json + saturated flag · output: _strategy.json (awareness, sof, jergas[], deseo, avatar, temporalidad, fuerza_cambio, formato, antagonistas, need_complex_videos)",
          "input_from": "analizar.analizador-video",
          "output_to": "buscar.* (todos los buscadores en paralelo)",
          "verifier_l1": "verify_search_strategy.json (campos: awareness, sof, jergas[], deseo, formato)",
          "supervisor_l2_skill": "Detectar si la estrategia está vaga o conflictiva con el video ganador",
          "canonical_agent_name": "interpretador-estrategia-busqueda",
          "canonical_agent_md": ".claude/agents/escalado_formatos/interpretador-estrategia-busqueda.md"
        }
      ],
      "supervisor": {
        "id": "supervisor-interpretar",
        "name": "Supervisor Intérprete",
        "scope": "sub-proceso completo interpretar"
      }
    },
    {
      "id": "buscar",
      "label": "BUSCAR",
      "sublabel": "FB + GetHookd · 4 paralelos · BUCLE buscar→transcribir→verificar",
      "order": 4,
      "position": {
        "x": 8,
        "z": -10
      },
      "mat_color": "#1abc9c",
      "border_color": "#16a085",
      "internal_loop": {
        "description": "Cada buscador ejecuta INTERNAMENTE el bucle: buscar → descargar → transcribir → verificar match con estrategia. Si NO match → busca de nuevo con queries alternativas. Hasta encontrar N candidatos válidos o agotar reintentos.",
        "max_retries_per_agent": 5,
        "min_valid_candidates_per_agent": 2,
        "verify_match_on": [
          "awareness",
          "sof",
          "jergas",
          "deseo_principal",
          "avatar_match",
          "temporalidad",
          "fuerza_cambio"
        ]
      },
      "saturated_market_mode": {
        "trigger": "input.saturated === true OR strategy.need_complex_videos === true",
        "rule": "Mercado saturado = el ad corto vendiendo el producto directo YA NO ESCALA. Filtrar candidatos MÁS CURRADOS · formato AMPLIO, no condicionado a una estructura única.",
        "audiencia_saturada_def_msg4727": "Público que SABE que tiene un problema pero NO ACTÚA (cree no grave) · O persona que NO RECONOCE el problema pero le MOLESTA en el día a día. Refinado verbatim Fer voice msg 4727 2026-05-09.",
        "what_to_REJECT": [
          "Ad corto que vende el producto directamente (UGC talking-head básico problem→solution sin más capas)",
          "Producto-shot directo sin gancho narrativo",
          "Voz over genérica leyendo features sin contexto"
        ],
        "what_to_ACCEPT_signals_amplio": [
          "VSL larga (preferido)",
          "Advertorial / informativo",
          "Comparativas (vs alternativas / vs competidores / antes-después)",
          "Storyselling personaje + revelación",
          "Parodia / humor / asociación lateral",
          "Lifestyle aspiracional con producto integrado",
          "Mecanismo revelado con jerga técnica creíble",
          "Voz over que apila beneficios encadenados con cadencia",
          "Demo visual con drama (efecto antes/después fuerte)",
          "Anuncio vertical estilo TikTok con hook fuerte primeros 3s"
        ],
        "criterion": "más currado / más trabajado / concepto más elaborado y buscado · NO ad corto vendiendo producto directo",
        "duration_preference": "Tendencia: videos LARGOS con concepto elaborado. NO condicionada estricta — puede ser corto si tiene capas (storyselling, mecanismo, comparativa, etc).",
        "if_not_saturated": "Acepta también UGC/talking-head simples (mercado no saturado tolera formatos básicos)",
        "cascada_paises_msg644_REGLA_141_REFINADA": "1) PAÍS INPUT primero (input.country: ES/MX/etc · donde se vende COD) → 2) UK/USA/AU/CA fallback → 3) Otros países último recurso. NUNCA excluir país input."
      },
      "agents": [
        {
          "id": "buscador-fb-1",
          "name": "Buscador FB #1",
          "skill": "BUCLE: busca en Facebook Ads Library → descarga candidato → transcribe Whisper → verifica match awareness/sof/jergas/deseo con ganador → si MATCH guarda; si NO MATCH busca de nuevo con query alternativa. Máx 5 reintentos. Min 2 candidatos válidos.",
          "backend_skill": "apify-fb-ads (skill canónica para FB Ads Library mining) + faster-whisper para transcripción",
          "backend_invocation": "Skill apify-fb-ads con queries derivadas de _strategy.json · resultados → cada video se baja + whisper + se valida match · output _candidates_fb_1.json",
          "input_from": "interpretar.interpretador-video",
          "output_to": "comparador.comparador-awareness",
          "parallel_group": "buscadores",
          "verifier_l1": "verify_fb_search_results.py (≥2 candidatos validados + match awareness/sof/jergas)",
          "supervisor_l2_skill": "Detectar si el bucle se atasca (5 reintentos sin match) o si está aceptando candidatos chatarra como match",
          "canonical_agent_name": "buscador-paralelo-fb-gethookd",
          "canonical_agent_md": ".claude/agents/escalado_formatos/buscador-paralelo-fb-gethookd.md"
        },
        {
          "id": "buscador-fb-2",
          "name": "Buscador FB #2",
          "skill": "Igual que FB#1 pero con keywords/queries alternativas para diversificar (mismo bucle interno buscar→transcribir→verificar→retry)",
          "backend_skill": "apify-fb-ads (segunda instancia paralela con queries alternativas)",
          "backend_invocation": "Idem FB#1 con queries diferentes coordinadas vía shared_state.json",
          "input_from": "interpretar.interpretador-video",
          "output_to": "comparador.comparador-awareness",
          "parallel_group": "buscadores",
          "verifier_l1": "verify_fb_search_results.py",
          "supervisor_l2_skill": "Igual que FB#1",
          "canonical_agent_name": "buscador-paralelo-fb-gethookd",
          "canonical_agent_md": ".claude/agents/escalado_formatos/buscador-paralelo-fb-gethookd.md"
        },
        {
          "id": "buscador-gethookd-1",
          "name": "Buscador GetHookd #1",
          "skill": "BUCLE: busca en GetHookd.ai → descarga candidato → transcribe → verifica match → retry si no match. Máx 5 reintentos. Min 2 candidatos válidos.",
          "backend_skill": "video-referencia-hunter (subagente canónico para GetHookd · ya implementa bucle buscar→descargar→whisper→validar). CONFIRMO_LECTURA_AGENTE_CANONICOS",
          "backend_invocation": "Task tool con subagent_type=video-referencia-hunter · input: _strategy.json · output: _candidates_gh_1.json",
          "input_from": "interpretar.interpretador-video",
          "output_to": "comparador.comparador-awareness",
          "parallel_group": "buscadores",
          "verifier_l1": "verify_gethookd_search_results.py",
          "supervisor_l2_skill": "Detectar si el bucle se atasca o acepta chatarra",
          "canonical_agent_name": "buscador-paralelo-fb-gethookd",
          "canonical_agent_md": ".claude/agents/escalado_formatos/buscador-paralelo-fb-gethookd.md"
        },
        {
          "id": "buscador-gethookd-2",
          "name": "Buscador GetHookd #2",
          "skill": "Igual que GH#1 con queries alternativas",
          "backend_skill": "video-referencia-hunter (segunda instancia paralela). CONFIRMO_LECTURA_AGENTE_CANONICOS",
          "backend_invocation": "Idem GH#1 con queries diferentes coordinadas vía shared_state.json",
          "input_from": "interpretar.interpretador-video",
          "output_to": "comparador.comparador-awareness",
          "parallel_group": "buscadores",
          "verifier_l1": "verify_gethookd_search_results.py",
          "supervisor_l2_skill": "Igual que GH#1",
          "canonical_agent_name": "buscador-paralelo-fb-gethookd",
          "canonical_agent_md": ".claude/agents/escalado_formatos/buscador-paralelo-fb-gethookd.md"
        }
      ],
      "supervisor": {
        "id": "supervisor-buscar",
        "name": "Supervisor Buscadores",
        "scope": "grupo paralelo de 4 buscadores · garantiza diversidad entre los 4 · detecta cuando algún buscador se atasca en su bucle interno y propone queries alternativas"
      },
      "parallel_communication": {
        "rule": "Los 4 buscadores SE HABLAN entre ellos en tiempo real para no buscar lo mismo. Antes de lanzar query, cada buscador consulta el shared_state.json del sub-proceso para ver qué queries y qué video_ids ya cubren los demás. Si su query/id está cubierto → genera variante alternativa.",
        "shared_state_file": "_buscar/shared_state.json",
        "fields": [
          "queries_in_progress",
          "queries_done",
          "video_ids_already_evaluated",
          "candidates_found_so_far"
        ],
        "applies_to_other_parallel_groups": true
      }
    },
    {
      "id": "comparador",
      "label": "COMPARADOR",
      "sublabel": "Match awareness/jergas/deseo con ganador",
      "order": 5,
      "position": {
        "x": 16,
        "z": -4
      },
      "mat_color": "#e74c3c",
      "border_color": "#c0392b",
      "agents": [
        {
          "id": "comparador-awareness",
          "name": "Comparador",
          "skill": "Compara cada video candidato vs el video ganador del input. Verifica match en: awareness, sof, jergas, deseo principal, formato. Decide: 5 SIMILARES (alta coincidencia) + 1 ARRIESGADO (similar pero con un giro). Si <6 candidatos válidos → relanza buscadores.",
          "backend_skill": "agente-esceptico (revisa output crítico) + Claude Opus inline call para scoring",
          "backend_invocation": "Task subagent_type=agente-esceptico · input: _candidates_*.json + _analysis.json (ganador) · output: _finalists.json con 5 similares + 1 arriesgado",
          "input_from": "buscar.* (consolidado)",
          "output_to": "trello_editor.creador-trello",
          "verifier_l1": "verify_6_finalists.py (5 similares + 1 arriesgado, todos awareness-match)",
          "supervisor_l2_skill": "Detectar si el comparador está siendo demasiado laxo o demasiado estricto",
          "canonical_agent_name": "comparador-finalistas",
          "canonical_agent_md": ".claude/agents/escalado_formatos/comparador-finalistas.md"
        }
      ],
      "supervisor": {
        "id": "supervisor-comparador",
        "name": "Supervisor Comparador",
        "scope": "sub-proceso completo comparador"
      }
    },
    {
      "id": "trello_editor",
      "label": "TRELLO EDITOR",
      "sublabel": "Crear tarjeta para editor humano",
      "order": 6,
      "position": {
        "x": 14,
        "z": 8
      },
      "mat_color": "#27ae60",
      "border_color": "#0a7c3a",
      "agents": [
        {
          "id": "creador-trello",
          "name": "Creador Trello",
          "skill": "Crea tarjeta en Trello del board editor de video con: 6 mp4 adjuntos + brief de cada uno + análisis del ganador como contexto + URLs originales",
          "backend_skill": "Python script + Trello API (board rH2kazG2 editor de videos) · idéntico al patrón en `_factory_3d_demo/_create_trello_card.py` · CONFIRMO_LECTURA_AGENTE_CANONICOS feedback_brief_editor_3_secciones",
          "backend_invocation": "Bash: python create_trello_card.py · key+token Trello · attach 6 mp4 + 6 PDFs brief (3 secciones canónicas) + cover input · POST /cards",
          "input_from": "comparador.comparador-awareness",
          "output_to": "OUTPUT (tarjeta Trello creada · short_url devuelta)",
          "verifier_l1": "verify_trello_card_complete.py (6 attachments + descripción no vacía + label correcto)",
          "supervisor_l2_skill": "Detectar si la tarjeta está incompleta o mal formateada",
          "canonical_agent_name": "creador-trello-editor-videos",
          "canonical_agent_md": ".claude/agents/escalado_formatos/creador-trello-editor-videos.md"
        }
      ],
      "supervisor": {
        "id": "supervisor-trello-editor",
        "name": "Supervisor Trello",
        "scope": "sub-proceso completo trello editor"
      }
    }
  ],
  "master_supervisor": {
    "id": "master-mejora-continua",
    "name": "Master Mejora Continua",
    "appearance": "monigote con gorro negro alto y bastón",
    "scope": "global · agrega reportes de los 6 supervisores de sub-proceso · CBR macro · propone REGLAs sistémicas",
    "position": {
      "x": 0,
      "z": 4
    },
    "cbr_storage": "_master_cbr_db.json"
  },
  "connections_serial": [
    [
      "descargar",
      "analizar"
    ],
    [
      "analizar",
      "interpretar"
    ],
    [
      "interpretar",
      "buscar"
    ],
    [
      "buscar",
      "comparador"
    ],
    [
      "comparador",
      "trello_editor"
    ]
  ],
  "fatal_error_escalation_chain": {
    "philosophy": "El sistema funciona de manera AUTÓNOMA. Ante un error fatal en cualquier estación, la línea para y se intenta resolver SIEMPRE eligiendo el camino más largo, riguroso, racional — apoyado en el libro Breakthrough Advertising de Eugene Schwartz + lógica del CEO. NUNCA atajos.",
    "order": [
      "1. Supervisor de la LÍNEA (sub-fase) intenta resolver localmente con su CBR + verifier",
      "2. Si no resuelve → escala al SUPERVISOR DEL PROCESO ENTERO (sub-proceso)",
      "3. Si no resuelve → escala al MASTER MEJORA CONTINUA (gorro negro)",
      "4. Si no resuelve → escala a FER (CEO humano) vía Telegram"
    ],
    "principles_canonical_books": [
      "Breakthrough Advertising — Eugene Schwartz",
      "Lógica + reglas del CEO en CLAUDE.md + memorias persistentes"
    ]
  },
  "delivery_showcase": {
    "description": "Vitrina visual donde se acumulan las entregas finalizadas. Cada vez que un producto/flow termina exitosamente la última estación, se añade a la vitrina con miniatura + metadata. Visible siempre en la fábrica 3D.",
    "position": {
      "x": 24,
      "z": 12
    },
    "max_visible": 12,
    "fields_per_entry": [
      "id",
      "product_name",
      "completed_at",
      "thumb_url",
      "trello_card_url",
      "video_count"
    ]
  },
  "parallel_flows_note": "Esta es UNA fábrica con UN flow (escalado-formatos). Cuando lleguen más flows, irán como columnas paralelas en la misma planta — cada flow su propia secuencia de estaciones. Se accede al flow correspondiente vía la posición XZ. La vitrina y el master son COMPARTIDOS entre todos los flows.",
  "default_model_policy": {
    "rule": "TODOS los agentes ejecutores y supervisores usan claude-opus-4-7 por defecto. EN CASO DE DUDA, SIEMPRE Opus. Sin excepciones para tareas de razonamiento (búsqueda, comparación, análisis, generación de speech). Solo se permite modelo más bajo si la tarea es PURAMENTE mecánica (ej: copiar archivos, hacer una llamada API directa sin lógica de decisión) — y aun así Fer prefiere Opus para no fallar nunca.",
    "rationale": "Fer msg 4658 (2026-05-09 verbatim): 'asegúrate que todos los agentes utilizan opus, no quiero que literalmente falles porque veo mucho modelo de haiku que es el más barato y no tiene sentido. Ejemplo: el que está buscando en facebook y en gethookd en paralelo todos ellos tienen que ser opus. Todos los agentes utilizan opus a menos que sea una tarea súper simple. En caso de dudas siempre opus. Prefiero gastar a tener un trabajo mal hecho.'",
    "models": {
      "L1_executor": "claude-opus-4-7",
      "L2_supervisor_haiku_only_for_smell_test": "claude-haiku-4-5-20251001",
      "L3_supervisor_opus": "claude-opus-4-7",
      "L4_master_opus": "claude-opus-4-7",
      "doubt_escalation": "claude-opus-4-7"
    }
  },
  "bottleneck_scaling_policy": {
    "rule": "Si un sub-proceso se convierte en cuello de botella (cola entrante crece más rápido que se vacía), el Master Mejora Continua puede CLONAR el agente principal y desplegarlo en paralelo dentro del mismo sub-proceso para absorber carga.",
    "trigger": "queue_in > 3× promedio histórico durante ≥10min",
    "implementation": "Master spawnea N copias del agente original con el mismo skill + verifier · cada copia toma una task de la cola · supervisor del sub-proceso reparte rondas robin · cuando la cola baja a normal, las copias se eliminan",
    "limit_max_clones_per_subprocess": 5
  },
  "supervision_layers": {
    "L1_verifier": {
      "type": "deterministic_python",
      "cost": "free",
      "always_runs": true,
      "purpose": "Comprueba reglas duras conocidas. PASS/FAIL_<reason>."
    },
    "L2_haiku_supervisor": {
      "type": "claude_haiku",
      "cost": "low",
      "always_runs": true,
      "purpose": "Olfato rápido + abierto. FLAG_<reason> | CLEAN. Captura errores nuevos que L1 no contempla."
    },
    "L3_opus_subprocess_supervisor": {
      "type": "claude_opus",
      "cost": "high",
      "always_runs": false,
      "trigger": "L1 FAIL OR L2 FLAG",
      "purpose": "Razonamiento profundo. Propone fix, escribe en CBR del sub-proceso, añade verifier L1 si descubre patrón nuevo."
    },
    "L4_master": {
      "type": "claude_opus",
      "cost": "high",
      "always_runs": false,
      "trigger": "≥3 escalaciones de L3 en ventana 24h OR diario por cron",
      "purpose": "Detecta patrones cross-sub-proceso, propone REGLAs sistémicas, requiere aprobación Fer (human-in-loop)."
    }
  },
  "visual_appearance": {
    "agent": "monigote básico (cuerpo capsula + cabeza esfera + brazos + piernas) · color por estación",
    "supervisor": "monigote con gorro amarillo cilíndrico encima de la cabeza",
    "master": "monigote con gorro negro alto + bastón en mano"
  }
}