
    ^i&                      U d Z ddlmZ ddlZddlZddlZddlZddlZddlZddl	Z	ddl
mZ ddlmZmZ  ee      j                  Zedz  Zedz  Zedz  Zed	z  Zed
z  ZdZdZdZdZd Zd Zd7dZd8d9dZd:dZ G d d      Z G d d      Z  G d d      Z! G d d      Z"	 d;	 	 	 	 	 d<dZ#d=dZ$	 	 	 	 	 	 d>dZ%	 	 	 	 	 	 	 	 d?dZ&d@d Z'da(d!e)d"<   d# Z*d$ Z+dAd%Z,	 dB	 	 	 dCd&Z-dDdEd'Z.dDdEd(Z/dDdEd)Z0dDdEd*Z1dDdEd+Z2dDdEd,Z3e.e/e0e1e2e3d-Z4	 d8	 	 	 dFd.Z5edf	 	 	 	 	 dGd/Z6 G d0 d1      Z7dHdId2Z8d3 Z9e:d4k(  r$ejv                  jy                  d56        e9        yy)JuQ  orchestrator_v2 — Factory 3D Escalado Formatos · CONTRATO FACTRY 2026-05-09
============================================================================

Wire actualizado al contrato cristalizado en chat Fer 2026-05-09 (msgs 4684–4701):

  1. CAJA ÚNICA MUTABLE — un solo `caja.json` por flow que viaja de estación
     en estación. Cada estación lee la caja entera, escribe SU sección. NO
     archivos sueltos `_analysis.json`, `_strategy.json`, etc.

  2. SUPERVISOR BLOQUEA + RETROLOOP — tras cada estación, supervisor (L1+L2,
     escalando a L3 en flag) audita lo escrito en la caja. Si BREAK → re-spawn
     ejecutor con feedback inline. max_retries=5 default.

  3. SHARED_STATE.JSON BUSCAR×4 — los 4 buscadores en paralelo coordinan
     antes de cada query: leen `_buscar_shared_state.json`, evitan duplicar
     queries/video_ids, escriben sus claims antes de avanzar.

  4. DRY MODE (--dry-run) — smoke test sin LLM/network/Trello reales.
     Ejecutores generan fixtures plausibles, supervisor siempre APPROVE.

Modo de uso:

    # Smoke test interno (sin red, sin LLM, sin Trello):
    python orchestrator_v2.py --dry-run --from-inbox _flow_inbox/<file>.json --once

    # Run real (cuando los stubs estén conectados a subagentes/skills):
    python orchestrator_v2.py --from-inbox _flow_inbox/<file>.json

REGLAS DURAS HARDWIRED:
  - 1 agente = 1 skill (factory_metadata.json es la fuente de verdad)
  - Supervisor BLOQUEA · max_retries=5 · sin atajos
  - Caja única mutable · append/merge por sección · NUNCA dispersar
  - Default Opus en cualquier llamada LLM (msg 4658)
    )annotationsN)Path)datetimetimezonezfactory_metadata.json_flow_inbox_runsz_task_state.jsonz_orchestrator_stop.flag      z.8345671365:AAFNAmnzIBleXEOMvMOiYFUR6xkLU_Mehf8
6002482241c                 d    t        j                  t        j                        j	                         S N)r   nowr   utc	isoformat     @C:\Users\ferna\proyecto cero\_factory_3d_demo\orchestrator_v2.pynow_isor   A   s    <<%//11r   c                 B    t        t        j                         dz        S )N  )inttimer   r   r   now_msr   D   s    tyy{T!""r   c                Z    t        j                  dd|       j                  d      } | xs dS )Nz[^a-zA-Z0-9_-]+_flow)resubstrip)ss    r   slugifyr!   G   s*    
!3*005A;r   c                    t        | j                  dd            }| j                  dt                     j                  dd      j                  dd      }| d| }||j                  }t        |      S )Nproductunknown	timestamp:-.r   )r!   getr   replacestem)inbox_payload
inbox_pathr#   tsbases        r   derive_flow_idr0   K   sq    m''	9=>G			;		2	:	:3	D	L	LSRU	VBYatD4=r   c                 T    t        j                  t        j                  d            S Nutf-8encoding)jsonloads	META_PATH	read_textr   r   r   	load_metar:   S   s    ::i))7);<<r   c                  J    e Zd ZdZd
dZddZddZddZddZdddZ	ddZ
y	)CajauE   Documento único mutable por flow. JSON con secciones por station_id.c                    || _         || _        |dz  | _        | j                  j                  dd       | j                  j	                         s"|t               |i g d}| j                  |       y y )Nz	caja.jsonTparentsexist_ok)flow_id
created_atinput_startsectionshistory)rA   run_dirpathmkdirexistsr   _save)selfrA   rF   initial_inputinitials        r   __init__zCaja.__init__]   sn    k)	4$7yy!"%i,G JJw "r   c                `    t        j                  | j                  j                  d            S r2   r6   r7   rG   r9   rK   s    r   _loadz
Caja._loadl   #    zz$))--w-?@@r   c                j    | j                   j                  t        j                  |dd      d       y NFr	   ensure_asciiindentr3   r4   rG   
write_textr6   dumps)rK   docs     r   rJ   z
Caja._saveo   s'    		TZZ%JU\]r   c                "    | j                         S r   )rR   rQ   s    r   	read_fullzCaja.read_fullr   s    zz|r   c                d    | j                         }|j                  di       j                  |      S )NrD   )rR   r)   )rK   keyr\   s      r   read_sectionzCaja.read_sectionu   s)    jjlwwz2&**3//r   c                8   | j                         }|j                  di       }|j                  |      }|dk(  r3||g||<   nlt        |t              r|j                  |       nJ||g||<   nB|dk(  r8t        |t              r"t        |t              r|j                  |       n|||<   n|||<   |j                  dg       j                  t               |||t        t        j                  |j                  |      d            d       | j                  |       y)	zmode: replace | append | mergerD   appendNmergerE   FrW   )r.   actorsectionmodebytes_after)rR   
setdefaultr)   
isinstancelistrc   dictupdater   lenr6   r[   rJ   )rK   r`   valuerh   rf   r\   rD   existings           r   write_sectionzCaja.write_sectiony   s    jjl>>*b1<<$8!&Hd+&!)5 1W_(D)j.E& %!HSMy"%,,)tzz(,,s*;%PQ.
 	 	

3r   c                    | j                   j                         r$| j                   j                         j                  S dS )Nr   )rG   rI   statst_sizerQ   s    r   
size_byteszCaja.size_bytes   s-    +/99+;+;+=tyy~~''D1Dr   N)rA   strrF   r   rL   rm   returnrm   )r\   rm   )r`   rw   ry   zdict | None)r*   r$   )r`   rw   rp   rm   rh   rw   rf   rw   )ry   r   )__name__
__module____qualname____doc__rN   rR   rJ   r^   ra   rr   rv   r   r   r   r<   r<   Z   s+    O A^08Er   r<   c                  8    e Zd ZdZddZd	dZd
dZddZddZy)SharedStateu   JSON compartido entre los N agentes paralelos del cluster.
    Patrón equivalente a `buscador-paralelo-fb-gethookd._buscar/shared_state.json`.c                   || _         | j                   j                         sj| j                   j                  j                  dd       | j                   j	                  t        j                  t               g g g g i ddd      d       y y )	NTr>   )rB   queries_in_progressqueries_donevideo_ids_already_evaluatedcandidates_found_so_farclaimsFr	   rV   r3   r4   )rG   rI   parentrH   rZ   r6   r[   r   )rK   rG   s     r   rN   zSharedState.__init__   s    	yy!II""4$"?II  %i') "/1+-- "!"- 8? ! @ "r   c                `    t        j                  | j                  j                  d            S r2   rP   rQ   s    r   rR   zSharedState._load   rS   r   c                j    | j                   j                  t        j                  |dd      d       y rU   rY   )rK   ds     r   rJ   zSharedState._save   s'    		TZZaHSZ[r   c                    | j                         }||d   v s||d   v ry|d   j                  |       |d   j                  |g       j                  |t               d       | j	                  |       y)zIReturns True si pudo tomar la query, False si ya estaba en progreso/done.r   r   Fr   )qr.   T)rR   rc   rj   r   rJ   )rK   agent_idqueryr   s       r   
take_queryzSharedState.take_query   sr    JJLA+,,>9J0J	
 ''.	(x,33%wy4QR

1r   c                    | j                         }||d   v r|d   j                  |       ||d   vr|d   j                  |       |D ]  }||d   vs|d   j                  |         |d   j                  |       | j	                  |       y )Nr   r   r   r   )rR   removerc   extendrJ   )rK   r   r   	video_ids
candidatesr   vids          r   finish_queryzSharedState.finish_query   s    JJLA+,,#$++E2.))n$$U+C!9::/077<  	

#$++J7

1r   N)rG   r   rx   )r   rm   )r   rw   r   rw   ry   bool)r   rw   r   rw   r   	list[str]r   z
list[dict])	rz   r{   r|   r}   rN   rR   rJ   r   r   r   r   r   r   r      s$    X@A\
r   r   c                  4    e Zd ZdZdddddddZddd	Zdd
Zy)
SupervisoruR  Wrapper L1 (verifier determinista) + L2 (Haiku olfato) + L3 escalación Opus.

    Cada supervisor lleva su PDF sub-proceso como audit base canónica
    (`_pdfs/PDF_<STATION>.pdf` · msg 4717+4723 Fer 2026-05-09).
    El PDF define literalmente qué validar — supervisor NO inventa criterios.

    En DRY mode: L1 valida que la sección exista y no esté vacía + PDF presente → APPROVE.
    En modo real: L1 ejecuta verifier_l1 declarado en metadata; L2 invoca Haiku
    con el PDF como contexto; L3 escala a Opus si L2 levanta flag o L1 falla,
    también con el PDF como contexto.
    zPDF_DESCARGAR.pdfzPDF_ANALIZAR.pdfzPDF_INTERPRETAR.pdfzPDF_BUSCAR.pdfzPDF_COMPARADOR.pdfzPDF_TRELLO_EDITOR.pdf	descargaranalizarinterpretarbuscar
comparadortrello_editorc                   || _         || _        |j                  di       j                  dd      | _        |d   | _        | j
                  j                  | j                        }|rt        dz  |z  | _        y d | _        y )N
supervisoridsup_unknown_pdfs)station_defdry_runr)   supervisor_id
station_idPDF_NAME_MAPROOTpdf_audit_base)rK   r   r   pdf_names       r   rN   zSupervisor.__init__   sq    &(__\2>BB4W%d+$$((9=Etg~84r   c                   |j                  | j                        }|dd| j                   dfS t        |t              r|sdd| j                   dfS t        |t              r t        |      dk(  rdd| j                   dfS | j                  r+| j                  j                         sdd| j                   fS | j                  r.| j                  rd	| j                  j                   nd
}dd| fS y)uN   Devuelve (verdict, reason).
        verdict ∈ {APPROVE, BREAK_<reason_code>}BREAK_EMPTY_SECTIONzcaja.sections.z
 no existez es {}r   z es []BREAK_PDF_BASE_MISSINGu"   PDF base de auditoría no existe: u    · base PDF:  APPROVEdry_run_pass)r   real_pass_stub)
ra   r   rk   rm   rl   ro   r   rI   r   name)rK   cajarg   r/   s       r   auditzSupervisor.audit   s     ##DOO4?)^DOO;LJ+WXXgt$W)^DOO;LH+UVVgt$W):)^DOO;LF+STTt':':'A'A'C,0RSWSfSfRg.hii<<BFBUBU^D$7$7$<$<#=>[]DdV455
 -r   N)F)r   rm   r   r   )r   r<   ry   ztuple[str, str])rz   r{   r|   r}   r   rN   r   r   r   r   r   r      s-    
 )&,"*0LP-r   r   c                      e Zd ZdZddZd Zy)
Hypothesisu   Una hipótesis raíz para resolver un fallo. Lleva una idea humana + el
    feedback que se inyecta al ejecutor en el siguiente intento.c                <    || _         || _        || _        || _        y r   r   levelideafeedback)rK   id_r   r   r   s        r   rN   zHypothesis.__init__  s    
	 r   c                `    | j                   | j                  | j                  | j                  dS )Nr   r   rQ   s    r   to_dictzHypothesis.to_dict  s$    gg

DIISWS`S`aar   N)r   rw   r   rw   r   rw   r   rw   )rz   r{   r|   r}   rN   r   r   r   r   r   r     s    D!br   r   c           	     4   	 t        j                  dddddd| gdd|d	      }|j                  d
k7  ryt        j                  |j
                        }|j                  d      ry|j                  dd      j                         }|j                  d      r5t        j                  dd|t        j                        j                         }t        j                  |      }|j                  d      xs g }	t        |	      dk7  ryg }
t        |	d      D ]t  \  }}|j                  d      xs dj                         }|j                  d      xs dj                         }|r|s y|
j                  t        | d| |||             v |
S # t         $ r Y yw xY w)u   Llama a `claude --print --model opus --output-format json` con el prompt
    y parsea 3 hipótesis. Devuelve None si algo falla (caller hará fallback).
    Patrón: el outer JSON tiene `result` con el inner JSON string a parsear.claude--print--modelopus--output-formatr6   Tr3   )capture_outputtexttimeoutr5   r   Nis_errorresultr   ```^```(?:json)?\s*|\s*```$flags
hypotheses      )startr   r   r   r   r   )
subprocessrun
returncoder6   r7   stdoutr)   r   
startswithr   r   	MULTILINEro   	enumeraterc   r   	Exception)promptr   r   expected_ids_prefix	timeout_sprocouter
inner_textinner	hyps_dataoutihr   r   s                  r   _invoke_opus_for_hypothesesr     sz   
~~y)V5FPVWdI
 ??a

4;;'99Z YYx,224
  ' ;RSUS_S_`ffhJ

:&IIl+1r	y>Qiq1DAqEE&M'R..0Dj)/R668HxJJz&'q,e$  2 
 s*   0F 0F $BF A F $&F 	FFc                   | d   }|rYt        d| ddd| dd| d| d	
      t        d| ddd| dd| d| d
      t        d| ddd| dd| d| d
      gS t        j                  j                  |d      }|rt        dz  |z  nd}d}|r'|j                         r	 |j                  dd      dd }t        j                  |j                         j                  di       d      dd }d |j                          d!| d"| d#| d$| d%}	t        |	|dd| &      }
|
t        | ||d'(      S |
S # t        $ r d}Y w xY w))u@  QC de la estación → 3 hipótesis RAÍZ distintas entre sí.
    Distintas en el sentido de raíz causal, no rephrasings.
    DRY mode → 3 stubs canónicos.
    REAL mode → invoca Opus headless (claude --print) con prompt sólido; si falla
    parse o CLI, fallback a los stubs canónicos para no bloquear el flow.r   qc__1qcu   [QC raíz 1] PARÁMETROS — z  con valores demasiado agresivosu   
[RESCUE QC-H1 · u   ] El intento estándar falló: u   .
Hipótesis raíz 1 (PARÁMETROS): los parámetros usados son demasiado agresivos. Reintenta con valores conservadores y timeout x2 sobre el default. NO cambies de herramienta, NO cambies de input — solo afloja parámetros.r   _2u5   [QC raíz 2] HERRAMIENTA — la herramienta usada en z no aplica al casou   
[RESCUE QC-H2 · u   .
Hipótesis raíz 2 (HERRAMIENTA): la herramienta primaria no aplica a este caso concreto. Cambia a la alternativa documentada en el PDF de la estación (segundo método declarado). NO toques parámetros — solo cambia de método._3u+   [QC raíz 3] INPUT — el input que recibe u    está mal formateadou   
[RESCUE QC-H3 · u  .
Hipótesis raíz 3 (INPUT): el input que recibe la estación viene mal formateado / con caracteres raros / encoding inconsistente. Normaliza/valida input (strip, ASCII safe, schema check) ANTES de invocar la herramienta. Mismo método y parámetros que el intento estándar.r   r   Nr3   ignore)r5   errorsp  rD   Fre     u   Eres el QC de la estación uG    de la factoría escalado_formatos.
El sub-agente ejecutor falló con: ul  

Genera 3 HIPÓTESIS RAÍZ distintas entre sí (NO 3 rephrasings de la misma idea, tres causas raíz CAUSALMENTE distintas — p.ej. una sobre PARÁMETROS, otra sobre HERRAMIENTA/MÉTODO, otra sobre INPUT/DATOS), cada una con un fix concreto que el ejecutor de la estación pueda aplicar literalmente en el siguiente intento. No inventes pasos fuera del scope de u)   .

PDF base de la estación (recortado):
z

Caja del flow (recortada):
u1  

Devuelve SOLO un JSON con esta forma exacta, sin texto adicional, sin ```:
{"hypotheses":[{"idea":"<una línea humana describiendo la causa raíz 1>","feedback":"<instrucción literal al ejecutor con el fix concreto, máximo 4 líneas>"},{"idea":"...","feedback":"..."},{"idea":"...","feedback":"..."}]}r   Tr   )r   r   r   r)   r   rI   r9   r   r6   r[   r^   upperr   qc_generate_3_hypotheses)r   r   initial_breakr   sidr   pdf_pathpdf_excerptcaja_excerptr   hypss              r   r   r   C  s    d
CcU"t4SE9YZ/u4STaSb ch hj cU"tLSEQcd/u4STaSb cN NP cU"tB3%G\]/u4STaSb cR RT
 	
0 &&**33H,4tg~($HKHOO%	",,gh,OPUQUVK ::dnn.22:rBQVWX]Y]^L
%ciik] 3..;_ =0
 14u 522= ?''3n 5Q	R  'vsDPSTWSXkZD|'T=RVWWK-  	K	s   &D9 9EEc                   | d   }|r\t        d| ddd| dd| d| d	| d
      t        d| ddd| dd| d| d      t        d| ddd| dd| d| d      gS dj                  d |D              }t        j                  |j	                         d      dd }d|j                          d| d| d| d 	}t        ||dd| !      }	|	t        | |||d"#      S |	S )$u   MASTER QC central → 3 hipótesis NUEVAS, distintas de las 3 que QC ya probó.
    El master ve el flow entero (caja completa), no solo la estación.r   master_r   masteru5   [MASTER raíz 1] CROSS-FLOW — el fallo no está en z, viene de aguas arribau   
[RESCUE MASTER-H1 · u/   ] Las 3 hipótesis QC fallaron. Break inicial: u   .
Hipótesis cross-flow 1: el problema NO es de esta estación, viene de una estación previa que dejó datos malos en la caja. Lee caja.sections completa, identifica qué sección previa parece corrupta, y rehaz uQ    usando un input reconstruido a partir de input_start (no de la sección previa).r   r   u,   [MASTER raíz 2] FALLBACK GLOBAL — saltar z con modo degradadou   
[RESCUE MASTER-H2 · u  .
Hipótesis cross-flow 2: usa el FALLBACK a nivel flow para esta estación. Genera el output mínimo viable de la sección (cache previo, mock canónico, valores por defecto razonables) marcado con flag degraded_mode=True para que estaciones siguientes lo sepan, y sigue.r   u/   [MASTER raíz 3] CONTRATO — el supervisor de z aplica criterios incorrectosu   
[RESCUE MASTER-H3 · u  .
Hipótesis cross-flow 3: el contrato del supervisor de la estación está roto vs la realidad del input concreto. Re-lee el PDF base de la estación y la sección de la caja, decide si el output actual realmente cumple el espíritu del PDF, y si sí, marca la sección con override_supervisor_reason="<por qué pasa el espíritu del PDF aunque no la letra>" para que el supervisor apruebe.
c           	   3  V   K   | ]!  }d |d   d    d|d   d    d|d   dd   # yw)	z  - QC 
hypothesisr   z	 (FAIL): r       → detailN   r   .0as     r   	<genexpr>z/master_generate_3_hypotheses.<locals>.<genexpr>  sP      A !L/$'(	!L/&2I1J%PQRZP[\`]`PaObcs   ')Fre   N'  u   Eres el MASTER QC central de la factoría escalado_formatos. Ves el flow ENTERO, no solo la estación que falla. La estación que falla es z. Break inicial: uM   

El QC de la estación ya probó estas 3 hipótesis raíz y TODAS fallaron:
u  

Genera OTRAS 3 hipótesis distintas entre sí Y distintas de las 3 que ya probó QC. Tu nivel es CROSS-FLOW: piensa en (a) el problema viene de aguas arriba (otra estación dejó datos malos en la caja), (b) usar fallback global / modo degradado, (c) el contrato del supervisor está mal calibrado contra este input concreto, (d) re-arquitectura puntual. Cada hipótesis con fix concreto aplicable.

Caja completa del flow (recortada):
z

Devuelve SOLO JSON, sin texto, sin ```:
{"hypotheses":[{"idea":"...","feedback":"..."},{"idea":"...","feedback":"..."},{"idea":"...","feedback":"..."}]}r   Tr   )r   joinr6   r[   r^   r   r   master_generate_3_hypotheses)
r   r   r   qc_attemptsr   r   
qc_summary	caja_fullr   r   s
             r   r  r    s    d
C#b!8LSEQhi3C5 9,,9? ;r !E!r	tv #b!8CC5H[\3C5 9,,9? ;rrt #b!8FseKhi3C5 9,,9? ;qqs#
 	
6   J 

4>>+%@%HIEEHYY[M R' )WWaVb c0 1:{ ;Q	R  'vsHT[\_[`RabD|+K}kcghhKr   c                *   | d   }|j                   }dj                  d |D              }dj                  d |D              }	d|j                          d| d|dd	  d
| d|	 d|j                   d}
|j                  d| dz  }	 |j                  |
d       |ry	 ddlm} ddl	m
} |j                  t        |
dd      j                  d      }|j                  dt         d|ddi      }|j!                  |d      5 }d	|j"                  cxk  xr dk  nc cddd       S # t        $ r Y w xY w# 1 sw Y   yxY w# t        $ rH}	 |j                  d| dz  j                  t%        |      d       n# t        $ r Y nw xY wY d}~yd}~ww xY w) u   Ping SOS a Fer cuando QC+MASTER agotaron las 6 hipótesis sin solución.
    DRY mode: deja un marker file _telegram_SOS_<sid>.txt en run_dir (no llama API).
    REAL mode: POST a Telegram Bot API.r   r  c           	   3  P   K   | ]  }d |d   d    d|d    d|d   dd     yw)	z  QC r  r   r  r       · r  Nx   r   r  s     r   r
  z%telegram_alert_fer.<locals>.<genexpr>  sC      ZeUV%,%&eAhK=Qx[#=N<OPZe   $&c           	   3  P   K   | ]  }d |d   d    d|d    d|d   dd     yw)	z	  MASTER r  r   r  r   r  r  Nr  r   r  s     r   r
  z%telegram_alert_fer.<locals>.<genexpr>  sC      ^mYZ)AlOD)*%(}D8TcAR@ST^mr  u!   🚨 SOS factory_v4 · estación z bloqueada
flow_id: z
break inicial: N   u.   

QC probó 3 hipótesis raíz · todas FAIL:
u2   

MASTER probó otras 3 hipótesis · todas FAIL:
z

caja: z"
Necesito tu ayuda para destrabar._telegram_SOS_z.txtr3   r4   Tr   true)chat_idr   disable_web_page_previewzhttps://api.telegram.org/botz/sendMessagezContent-Typez!application/x-www-form-urlencoded)dataheaders
   r   ,  z
.error.txtF)rA   r  r   rG   rF   rZ   r   urllib.requestrequesturllib.parseparse	urlencodeTELEGRAM_CHAT_FERencodeRequestTELEGRAM_TOKEN_SOSurlopenstatusrw   )r   r   r   r  master_attemptsr   r   rA   
summary_qcsummary_masterr   marker_ur_upbodyreqres                     r   telegram_alert_ferr6    s   
 d
CllG Ze J YY ^m N ,CIIK= 99 '-. /88B| D<<J;K L ,	- 	 \\nSE66F$1 $"}}((.
  6'?	 	
 kk*+=*>lK#%HI  

 [[b[)Q!(((S( *)%  $ *) 	\\nSE<<HHQZaHb 		sg   D& A%E D5	E &	D21D25D>:E >E 	F-E98F9	FFFFFc                    | syd|  dS )Nr   u%   
[RETRY] Supervisor previo rechazó: u   . Corrige específicamente eso.r   )supervisor_breaks    r   _retry_feedback_textr9    s    34D3EEdeer   'TaskStateBridge | None'_GLOBAL_BRIDGEc                    | a y r   r;  )bs    r   _set_global_bridger?    s    Nr   c                     t         S r   r=  r   r   r   _bridgerA    s    r   c                    t               }|	 |j                  | |       yt        d| j	                          d|        y# t        $ r Y +w xY w)zHEscupe un log al HTML en vivo si hay bridge montado, sin bloquear si no.N[] )rA  
append_logr   printr   )r   r   r>  s      r   _emitrG  	  sU    	A}	LL% 
Aekkm_Btf
%&  		s   A 	AAc                z	   t        d| d|         d|  d| d}	 t        j                  dddd	d
ddd|g	t        j                  t        j                  dddd      }d}d}|j
                  D ]@  }|j                         }	|	s	 t        j                  |	      }
|
j                  dd      }|dk(  rs|
j                  di       }|j                  dg       D ]I  }|j                  d      dk(  r}|j                  dd      }t        j                  |j                  di       d      }t        d|  d|        t        dt        |      d       D ]  }t        d|  d!|||d z             |j                  d      d"k(  s|j                  d"d      j                         }|s|}|j                  d#      D ]g  }|j                         }|st        |      d k  rt        d|  d$|        6t        dt        |      d       D ]  }t        d|  d$|||d z             i L |d%k(  r |
j                  di       }|j                  dg       D ]  }|j                  d      d&k(  s|j                  dd      }t        |t               rd#j#                  d' |D              }t%        |      }|j                  d#      D ]g  }|j                         }|st        |      d k  rt        d|  d(|        6t        dt        |      d       D ]  }t        d|  d(|||d z             i  |d)k(  s|
j                  d)d      j                         }|j'                  d*      r5t)        j*                  d+d|t(        j,                  ,      j                         }	 t        j                  |      }C |j3                         }|?|r=t)        j.                  d-|      }|r%	 t        j                  |j1                  d            }|dk7  rddd.| fS |y/d|dfS # t        $ r Y w xY w# t        $ rQ t)        j.                  d-|      }|r5	 t        j                  |j1                  d            }n# t        $ r Y nw xY wY w xY w# t        $ r Y w xY w# t        $ r}ddd0t%        |      dd1  fcY d}~S d}~ww xY w)2u(  Spawnea el subagente canónico vía `claude --print --model opus` con stream-json.
    Cada evento del subagente (tool_use, text) se escupe al bridge en vivo.
    Devuelve (ok, parsed_result_dict, error_msg). El subagente debe terminar
    devolviendo en su mensaje final un JSON que parsearemos.infou#    · spawneando subagente canónico zUse the z agent to perform this task.

u   

CRITICAL: cuando el subagente termine, devuelve en tu mensaje final UN ÚNICO JSON sin texto adicional, sin ``` blocks, en una sola línea, con todos los campos pedidos.r   r   r   r   r   zstream-jsonz	--verbosez--include-partial-messagesTr3   r*   r   )r   stderrr   r5   r   bufsizer   Ntype	assistantmessagecontenttool_user   inputFre   u	    · 🔧 r   r  u    ·    r   r  r  usertool_resultc              3  b   K   | ]'  }t        |t              s|j                  d d       ) yw)r   r   N)rk   rm   r)   )r  xs     r   r
  z)_spawn_canonical_agent.<locals>.<genexpr>Z  s'     +bs!jYZ\`NaAEE&",=ss   //u    · ↩ r   r   r   r   \{[\s\S]*\}\s*$zclaude exit=)FNu%   subagente no devolvió JSON parseablezspawn exception:    )rG  r   PopenPIPESTDOUTr   r   r6   r7   r   r)   r[   rangero   splitrk   rl   r  rw   r   r   r   r   searchgroupwait)canonical_namer   r   r   full_promptr   	last_text
result_objrawlineevtmsgctnametinputr   r   lnr   out_sr   mrcr5  s                            r   _spawn_canonical_agentrp    s    
&ZL CNCSTU
>""B6( Ka 	b 
a? y)V)	
 ??:+<+<	1
 	
;;C99;DZZ% vr"AK ffY+B/AuuV}
2 !fb 1!%AEE'2,>U!Sf(8	%&IJ!&q#f+s!;A!&^,<GF1QsUOCT*UV "<v&0 uuVR0668(,I&*jj&6%'XXZ')$,#&r7c>$)&^4DD2M$N-21c"gs-C(-f8HRPQRSTWRW[M6Z([ .D '7 0, fffY+B/AuuV}5eeIr2%c40"&))+bs+b"bC #C"'++d"3B!#B#% ("2w#~ %f0@.M N).q#b'3)?A$)&^4DHRPQRSTWRW[M2Z$[ *@ #4 0 hx,224##E*FF#>EQSQ]Q]^ddfE	!!%E!2Jw H YY[)		,i8A!%AGGAJ!7J 7$,rd 333GZ##W  l ! !		"4e<A!)-AGGAJ)?J( ! !! !   ?d/At~>>>?s   A(R PC%R  "R #CR 'C	R 2A&R P'./R $R R R R 	P$ R #P$$R '!R	$Q.-R.	Q:7R9Q::R=R  RR 	RR RR 	R:R5/R:5R:c           
        | j                         j                  di       }|j                  d      xs |j                  d      }|j                  dd      }t        | j                  z  }|j	                  dd       |dz  }|st        d	d
       y|j                         ru|j                         j                  dkD  rX|j                         j                  dz  }	t        dd|	 d       | j                  ddt        |      |	|ddd       dd|	 dfS |r;t        dd|        | j                  dd|t        |      dz   d|xs dddd       y|rd| nd }
d!| d"| d#| d$|
 }t        d|dd%&      \  }}}|sd'|fS |j                  d(      }|rt        |      j                         sd'd)| fS |j                  d*      xs& t        |      j                         j                  dz  }	t        dd+|	 d,|j                  d-              | j                  dd't        |      |	||j                  d-      |xs dd.dd       dd/|	 d0|j                  d-       fS )1u6  Estación DESCARGAR: spawnea el subagente canónico descargador-video-escalado.
    El subagente decide qué herramienta usar (yt-dlp / Playwright MCP / servicio web FB
    downloader / inspect DOM) según funcione. Si recibe retry_feedback de la rescue ladder,
    el subagente lo aplica cambiando de método.rC   	video_urlurlr#   r$   Tr>   z	video.mp4erroru=   descargador-video-escalado · no hay video_url en input_start)Fno_video_urli i   successu+   descargador-video-escalado · cache hit · z KBr   )	cache_hit
video_pathsize_kbrs  r*   zdescargador-video-escaladorh   rf   zcache_hit (zKB)rI  uO   descargador-video-escalado · [DRY] spawnearía subagente canónico para bajar z.FAKEi  N)r   rs  rx  ry  retry_feedback_appliedTzDRY OKuI   

RETRY FEEDBACK (aplica este cambio si la herramienta anterior falló):
r   zDTarea: descargar el video Facebook de esta URL al disco local.
URL: z
Producto: z
Ruta de salida exacta: uD  

Métodos disponibles en cascada (usar el primero que funcione, fallback al siguiente si falla):
  1) yt-dlp con cookies-from-browser chrome
  2) MCP playwright: navegar a un servicio web FB downloader (ej. fdown.net, getfvid.com), pegar la URL, descargar el mp4 resultante con browser_evaluate o request directo
  3) MCP playwright: navegar a la URL FB original, inspeccionar DOM/network requests para extraer media_url firmado
Cuando termines, devuelve JSON: {"video_path":"<ruta absoluta>","size_kb":N,"method_used":"<yt-dlp|playwright_web|playwright_inspect>","duration_s":N}X  r   Frx  u*   subagente devolvió video_path no existe: ry  u+   descargador-video-escalado · mp4 listo · u    KB · método: method_used)rw  rx  ry  rs  r  r{  zdownloaded zKB via )r^   r)   RUNS_DIRrA   rH   rG  rI   rt   ru   rr   rw   rp  r   )r   r   r   retry_feedbackinprs  r#   out_dirout_pathry  feedback_noter   okr   errrx  s                   r   run_descargarr    s   
 ..


}b
1C
''+

0#''%.Cggi+G%GMM$M.$HgVW$ X]]_44w>--/))T1iFwisST;S]wWZ)
!= 	 	? {7)3///fghkglmn;Cs8}w7N[_&4&<)
 != 	 	?  xFbcqbrs  LNMu I ""* ,c /
	  --I6S^jmnOBczL)JT*-446B:,OOOjj#PZ(8(=(=(?(G(G4(OG	)B7)K[\b\f\fgt\u[vwx{#j/g6::m#<"0"8D% 9	  ;
 ;wiwvzz-/H.IJJJr   c                   | j                  d      xs i }|j                  d      }|sy| j                         j                  di       }|j                  dd      j                  dd      d	d
 }t        dz  |z  dz  }|r.t        dd       | j                  ddddddd|xs d	ddd       y|rd| nd}	d| d| d|j                  d d!       d"| d#|	 
}
t        d|
dd$%      \  }}}|sd|fS t        d&d'|j                  d(              | j                  di |||xs d	d)dd       dd*|j                  d+d,       d-fS ).u   Estación ANALIZAR: spawnea analizador-combo-completo. El subagente decide
    cache lookup, faster-whisper, frame extraction y síntesis combo.r   rx  )Fzno_video_path en caja.descargarrC   r#   r$    r   N   
_productosz_analysis.jsonrI  uM   analizador-combo-completo · [DRY] spawnearía subagente para análisis combor   TFu   DRY · sin trabajo real*   g      >@es)r   rw  
transcript
word_countduration_secondslanguage_detectedr{  r*   zanalizador-combo-completorz  r|  

RETRY FEEDBACK:
r   u7   Tarea: análisis combo completo del video.
video_path: z
producto: u   
país: countryESz
cache_path: u?  

Sigue el procedimiento del subagente: PASO 1 cache lookup → si miss, PASO 2 faster-whisper transcripción + ffmpeg frames + síntesis combo (awareness, sof, jergas, deseo, avatar, etc).
Cuando termines, devuelve JSON: {"cache_hit":bool,"transcript":"...","duration_seconds":N,"language_detected":"es","combo":{...}}  r~  rv  u7   analizador-combo-completo · análisis OK · cache_hit=rw  )rx  r{  u   analizado · r  ?r    )ra   r)   r^   r*   r   rG  rr   rp  )r   r   r   r  descrx  r  r#   
cache_pathr  r   r  r   r  s                 r   run_analizarr    s    [)/RD,'J7
..


}b
1Cggi+33C=crBG$w.1AAJfef:%?X$T&4&<(
 !<	 	 	>
 @N+N+;<TVM!l #I '')D)* +!l #5
 /		  --H&R\hklOBcz	)NvzzZeOfNghiz $Xf $XJ@N@VRV$X&.I  K =,>s!C DAFFFr   c                   | j                  d      xs i }| j                         j                  di       }|rBt        dd       | j	                  ddddd	gd
ddi|j                  dd      |xs dddd       y|rd| nd}t        j                  |d      dd }t        |j                  dd            }d|j                  dd       d|j                  dd       d| d| d | 
}	t        d|	dd!"      \  }
}}|
sd|fS t        d#d$|j                  d%       d&|j                  d'              | j	                  di |d(|xs didd       dd)|j                  d%       fS )*u   Estación INTERPRETAR: spawnea interpretador-estrategia-busqueda. El subagente
    arma la estrategia de búsqueda a partir del combo del análisis.r   rC   rI  u@   interpretador-estrategia-busqueda · [DRY] spawnearía subagenter   Tproduct_aware   jerga_azdormir mejoredadz40-55	saturatedFN)r   	awarenesssofisticacionjergasdeseo_principalavatarneed_complex_videosr{  r*   z!interpretador-estrategia-busquedarz  r|  r  r   re   i@  ue   Tarea: interpretar el análisis del video y producir la estrategia de búsqueda.

INPUT:
  producto: r#   
   
  país: r  r  z
  saturated: uE   

ANÁLISIS PREVIO (transcript del video ganador + combo extraído):
un
  

REGLAS DURAS (Fer 2026-05-09 voice msgs + REGLAS CLAUDE.md):
  R1) AWARENESS = UN SOLO VALOR canónico Schwartz (NO descripciones tipo "Problem→Solution"). Valores válidos exactos: "unaware" | "problem_aware" | "solution_aware" | "product_aware" | "most_aware". DECIDIR el awareness PRINCIPALMENTE de los PRIMEROS 15 SEGUNDOS del transcript (≈37 palabras). El resto del video puede mostrar reframes pero el AWARENESS DOMINANTE para targeting es el de la apertura.
  R2) DESEO_PRINCIPAL = UN ÚNICO DOLOR aislado (REGLA aislar-un-dolor-por-creativo msg4467). NO mezclar dolores compuestos tipo "dolor de cabeza Y dolor en la nuca". Si el video ataca cefalea matutina, el deseo es "despertarse sin cefalea matutina/dolor de cabeza al despertar" punto. NO añadir cervical/nuca/postural. REGLA #150: deseo emocional ≠ nombre producto (almohada cervical → deseo es CEFALEA, no "dolor cervical"). El deseo es el dolor que la apertura del video activa.
  R3) JERGAS deben incluir al menos 1 MASS EDUCATION explícitamente marcada (REGLA #112): palabra/frase que el avatar mismo usa cotidianamente para nombrar su problema (ej. "cefalea matutina", "dolor de cabeza al despertar", "los años no perdonan", "cervicales"). Marca la mass education con campo aparte.
  R4) NADA de awareness_secondary. UN solo awareness.
  R5) TEMPORALIDAD: cómo de urgente/crónico es el dolor (diaria/semanal/estacional/etapa-vida).
  R6) Si saturated=true: marca need_complex_videos=true → buscar videos LARGOS VSL/storytelling (>30s) porque la audiencia tiene el problema pero NO está dispuesta a comprar todavía, hay que agitar+educar. Si saturated=false: videos cortos talking-head normales OK.

Devuelve SOLO JSON sin texto adicional ni ```:
{"awareness":"<unaware|problem_aware|solution_aware|product_aware|most_aware>","awareness_evidence_first_15s":"<cita literal del transcript primeros ~37 palabras>","sofisticacion":<1-5>,"jergas":["..."],"jerga_mass_education":"<la palabra/frase mass education exacta>","deseo_principal":"<UN solo dolor aislado>","deseo_evidencia_video":"<cita literal del video que confirma este deseo>","avatar":{"edad":"...","genero":"...","contexto":"...","profesion":"..."},"temporalidad":"<diaria|semanal|estacional|etapa_vida>","fuerza_cambio":"...","antagonistas":["..."],"formato_referente":"...","duracion_referente_seg":<n>,"need_complex_videos":<true|false>,"match_criteria":{"awareness":"<MISMO valor que arriba>","deseo_keywords":["..."],"jergas_obligatorias":["..."],"sof_min":<n>,"sof_max":<n>,"duracion_min_seg":<n>},"queries":["query 1 producto exacto España","query 2 producto exacto UK","query 3 deseo cefalea matutina ES","..."]}r   r~  rv  u/   interpretador-estrategia-busqueda · awareness=r  z sof=r  r{  z
awareness=)	ra   r^   r)   rG  rr   r6   r[   r   rp  )r   r   r   r  r   r  r  analisis_summaryr  r   r  r   r  s                r   run_interpretarr    s      ,2H
..


}b
1CfXY=/A knw'UZ@[&4&<	+

 !D 	 	F @N+N+;<TVMzz(?FSWW[%01Iwwy,- .GGId+, -!{ #OO_N` #aAH /S)	 X --PRXZgsvwOBcz	)FvzzR]G^F__dekeoeop  fA  eB  C  D}&b&b1I>Ka]a&b&.Q  S:fjj56777r   c                
   t         | j                  z  dz  }t        |      }g d}|rt        dd       g }|D ]  }d| d}	|j	                  ||	      }
|
sd| d}	|j	                  ||	       t        d| d|	 d	       t        d
      D cg c]
  }d| d|  }}|D cg c]  }||dd
 }}|j                  ||	||       t        d| dt        |       d       |j                  |        | j                  ddt        |      |t        |      |dd |xs dddd       ddt        |       dfS | j                  d      xs i }t        j                  |d      dd }| j                         j                  d i       }|rd!| nd"}t!        |j                  d#d            }i }d$D ]  \  }}d%|v rd&nd'}d(| d)|j                  d*d"       d+|j                  d,d-       d.| d/| d0| d1| d2| d3| d4| }| d5}t        d| d6| d7       t#        j$                  d8d9d:d;d<d=|gt"        j&                  t"        j(                  dd>d?      ||<    g }|j+                         D ]  \  }}	 |j-                  d@A      \  }}|rt        j.                  |      ni }|j                  dBd"      j1                         }|j3                  dC      r5t5        j6                  dDd"|t4        j8                  E      j1                         }t5        j:                  dF|      }|r$t        j.                  |j=                  dG            ni }|j                  dHg       } t        d| dIt        |        dJ       |j                  |         | j                  d|t        |      |t        |      |xs ddNdd       ddOt        |       dPfS c c}w c c}w # t>        $ r(}!t        dK| dLt        |!      ddM         Y d}!~!d}!~!ww xY w)QuG   Cluster paralelo de 4 buscadores · coordinados vía shared_state.json.z_buscar_shared_state.json)buscador-fb-1buscador-fb-2buscador-gethookd-1buscador-gethookd-2rI  uK   buscar · arrancando 4 sub-agentes en paralelo (factory_metadata declara 4)fake_query__v1_v2u    · query "u'   " tomada del shared_state · ejecutandor	   vid_r   g333333?)r   video_idmatch_scorerv  r  z candidatos encontrados (DRY)r   TNr  )r   shared_state_path
agents_runcandidates_totalcandidates_sampler{  r*   buscadores_x4rz  u
   DRY OK · u&    candidatos · shared_state coordinador   Fre   r   rC   r  r   r  ))r  fb_1)r  fb_2)r  gh_1)r  gh_2fbz-FB Ads Library (Apify + meta_ads_library_api)zGetHookd (gethookd.ai)zWCONFIRMO_LECTURA_AGENTE_CANONICOS

Use the buscador-paralelo-fb-gethookd agent with id=u%  .

TAREA: buscar candidatos REALES escalados que coincidan con el combo ganador en awareness + deseo principal + temporalidad + jerga mass education. NO buscar genérico, NO buscar por categoría producto (ej. "almohada cervical"), buscar por el DESEO concreto del avatar.

INPUT:
  producto: r#   u   
  país DESTINO: r  r  z' (donde lanzaremos el ad)
  saturated: z
  fuente asignada: z
  shared_state_path: ur    (coordina con los otros 3 paralelos · NO duplicar queries/video_ids)

STRATEGY (interpretar combo del ganador):
u  

REGLAS DURAS canónicas (REGLA #141 + #91 + #30 + #112):
  R1) PAÍSES ORIGEN ESTRICTO: UK / USA / Australia. ES/PT/BR/India NO son origen válido. Cascada países: UK → USA → Australia. Si en UK no hay → USA → AU. NO incluir ES/PT/otros.
  R2) SCALED_DAYS sweet spot ESTRICTO 30-100 días (REGLA #141). <30d=raw sin validar, >100d=quemado/copiado. Ambos extremos DESCARTAR.
  R3) Cascada REGLA #46: producto EXACTO (almohada cervical igual) → producto SIMILAR (otra almohada o soporte cervical) → NICHO (productos cefalea matutina o dolor cabeza al despertar) → MACRO (productos COD dolor crónico avatar 35-60). NO saltar niveles. Documentar nivel cascada usado por candidato.
  R4) Awareness MATCH ESTRICTO: candidato.awareness == match_criteria.awareness (mismo valor canónico Schwartz, NO secundarios).
  R5) DESEO MATCH: candidato debe atacar el MISMO dolor aislado del ganador. Si ganador=CEFALEA, descartar candidatos que sean solo cervical/postural/mareos. REGLA aislar-un-dolor.
  R6) MASS EDUCATION REGLA #112: candidato debe mencionar al menos 1 jerga obligatoria en sus primeros 15 segundos del speech.
  R7) SATURACIÓN: si saturated=true O awareness=problem_aware/solution_aware con sof≥4 (catálogo público) → preferir videos LARGOS VSL/storytelling >30s para agitar+educar. Default mercado España COD productos saturados = VSL largo.
  R8) REGLA #30 sof 4: hooks tipo "el original / no es el típico / este sí sirve" → OK. Hooks tipo "acabo de descubrir / conoces este invento / nuevo en el mercado" → DESCARTAR (sof 1-2).
  R9) Devolver mínimo 2 candidatos válidos por buscador. Si no encuentras → expandir cascada países (UK→USA→AU) y nivel (exacto→similar→nicho→macro). Si tras cascada completa no hay 2 candidatos → flag insufficient + explicar qué descartaste y por qué.

Cada candidato debe traer:
  video_id, url, fuente, advertiser, country (UK/USA/AU), scaled_days (30-100), length_seconds, awareness, deseo_principal_detectado (cita primeros 15s), sofisticacion, jergas_detectadas, mass_education_match, formato, hook_apertura, cascada_nivel (exacto/similar/nicho/macro), match_score (0-1).

Devuelve SOLO JSON sin texto ni ```: {"agent_id":"z","fuente":"z","candidates":[...]}uI   

CRITICAL: termina devolviendo UN ÚNICO JSON sin texto adicional ni ```u.    · spawneando buscador-paralelo-fb-gethookd ()r   r   r   r   r   r6   r3   )r   rJ  r   r5   r   r  r  r   r   r   r   rV  r   r   u    · devolvió  candidatoswarnu    · falló: r  )r  r  r   r  r{  u
   buscar · z! candidatos totales (4 paralelos)) r  rA   r   rG  r   r[  r   ro   r   rr   rw   ra   r6   r[   r^   r)   r   r   rX  rY  rZ  itemscommunicater7   r   r   r   r   r   r]  r^  r   )"r   r   r   r  r  ssagentsall_candidatesr   r   tookjr   r   r   interpstrategy_summaryr  r  r  procs	search_idfuenter   ra  pr   r   r   r   rn  r  candsr5  s"                                     r   
run_buscarr  6  s    4<</2MM	&	'B]FfcdHhZs+A==1-D!(3/h*&XJk!4[\]8=aA14z1#.IAbklbk[^xSQUVbkJlOOHaJ?)zc*o->>[\]!!*-  	8!$%6!7  #N 3!/!3&4&<&
  	 	2 z#n"5!66\]]] }-3Fzz&u=etD
..


}b
1C@N+N+;<TVMSWW[%01IE b)DHIDU@[sCCL+ N
 779R01 2  #	$ 78 9%; '""( *$$5#6 7::J9K LB: CK<X^W__uoS) 	Z hab 	 	f
"PQZP[[\]^$**y)V5FP[\??:+<+<	
hg bp N{{}!	C]]3]/FC'*DJJsOEIIh+113E&:BR\\Z``b		,e4A-.4::aggaj)BDHH\2.E)zE
|;OP!!%( % 	x/$ !23"0"8D" _  . :c.122STTTK Blv  	C&XJl3q6$3<.ABB	Cs%   O"O2DO	PO==Pc           
        | j                  d      xs i }|j                  d      xs |j                  d      xs g }| j                  d      xs i }|rit        ddt        |       d       t        dd       | j	                  d	d
t        d      D cg c]  }d| |dk  rdndd c}dd|xs dddd       y|sy|rd| nd}dt        |       dt        j                  |d      dd  d t        j                  |d      dd!  d"| }	t        d|	d	d#$      \  }
}}|
sd|fS |j                  d%g       }|j                  d&      xs |j                  d'd(      }t        d)d*t        |       d+| d,       | j	                  d	|t        |      ||j                  d-d      |xs dd.dd       d
t        |       d/| d0fS c c}w )1uS   Estación COMPARADOR: 5 similares + 1 arriesgado a partir de los candidatos buscar.r   r   r  r   rI  u-   comparador-finalistas · [DRY] scoring sobre r  uC   comparador-finalistas · [DRY] elegiría 5 similares + 1 arriesgador   T   r  r
   similar
arriesgado)r  rolr   N)r   	finalistscount_similarescount_arriesgadosr{  r*   zcomparador-finalistasrz  )Tu   DRY OK · 5+1 finalistas)Fz)no hay candidatos en buscar para compararr  r   zTAREA: rankear los u    candidatos y devolver TOP 6 del MISMO awareness que el ganador (Fer msg 4886: ya NO 5+1, son 6 mismo awareness).

COMBO ganador completo (referencia única):
Fre     z

CANDIDATOS:
r  u_  

REGLAS DURAS para scoring (Fer msg 4891+4892+4893+4886):
  R1) El comparador SOLO verifica 4 dimensiones: awareness · deseo_principal · temporalidad · jergas. NO comparar speech literal (cambia entre videos). NO comparar formato per se.
  R2) FILTRO DURO: descartar todo candidato cuyo awareness != match_criteria.awareness. Los 6 finalistas DEBEN tener el mismo awareness canónico que el ganador.
  R3) FILTRO DURO: deseo_principal del candidato debe coincidir con el del ganador (UN dolor aislado). Si el ganador es cefalea matutina, descartar candidatos que sean solo cervical/postural/mareo (REGLA aislar-un-dolor).
  R4) FILTRO DURO: temporalidad coincide (diaria/semanal/etapa-vida).
  R5) FILTRO BLANDO: al menos 1 mass education jerga del ganador detectable en candidato (REGLA #112).
  R6) Score = 0.4·awareness_match + 0.3·deseo_match + 0.15·temporalidad_match + 0.15·jergas_match. Top 6 ordenados por score descendente. NO meter "arriesgado" como rol — todos son del mismo awareness.
  R7) Si <6 candidatos pasan filtros duros, devolver los que pasen + flag insufficient_candidates=true (no inventar candidatos).

Devuelve SOLO JSON sin texto: {"finalists":[{"video_id":"...","url":"...","awareness":"...","deseo_principal":"...","score":0-1,"reason":"verificación 4 dims"}],"insufficient_candidates":<bool>,"awareness_target":"<el awareness único>"}r}  r~  r  awareness_targetr  r  rv  u   comparador-finalistas · z finalistas mismo awareness=""insufficient_candidates)r  countr  r  r{  z finalistas (mismo awareness r  )	ra   r)   rG  ro   rr   r[  r6   r[   rp  )r   r   r   r  r   r   r  r   r  r   r  r   r  r  r  s                  r   run_comparadorr    sC   x(.BFL)RVZZ8K-LRPRJ}-3FfEc*oEVVabcf[\<inopiqriqdeaSzq1u)R^_iqr q&4&<	*

 !8 	 	: 0A@N+N+;<TVM
c*o. /88<

6X]8^_d`d8e7f g

:EB6EJK LX" /)	 . --DflfijOBcz

;+Izz"45TKPS9T	)0Y0@@]^n]oopqr|Y,#)::.G#O"0"8D& 4  6 C	N##@AQ@RRSTTT] ss   Gc                   | j                  d      xs i }|j                  dg       }| j                         j                  di       }| j                  d      xs i }|rBt        ddt	        |       d       | j                  dd	d
dt	        |      |xs dddd       y|rd| nd}|j                  d      xs |j                  dd      }	|j                  dd      }
|j                  dd      }|j                  dd      }t        j                         j                  d      }d|j                  dd       d|j                  d d!       d"| }d#t	        |       d$t	        |       d%|j                  dd       d&|j                  d d!       d'| j                   d(|	 d)|
 d*| d+| d,t        j                  |d-.      dd/  d0t        j                  |d-.      dd1  d2| d3| d4|	 d5| }t        d|dd67      \  }}}|sd-|fS t        d8d9|j                  d:              | j                  di |d;|xs didd       d	d<|j                  d:       fS )=u   Estación TRELLO_EDITOR: crea tarjeta real en board rH2kazG2 (editor de videos)
    con los 6 finalistas como adjuntos + brief PDF + cover.r   r  rC   r   rI  uJ   creador-trello-editor-videos · [DRY] crearía card en board rH2kazG2 con z finalistasr   TFAKE_CARD_123z"https://trello.com/c/FAKE_CARD_123N)r   card_id	short_urlattachments_countr{  r*   zcreador-trello-editor-videosrz  )Tu   DRY OK · tarjeta fake creadar  r   r  r  r  r  jerga_mass_educationduracion_referente_segr  z%Y-%m-%dz[ESCALADO FORMATOS] r#   Productor  r  r  u$    · 6 finalistas mismo awareness · zCONFIRMO_LECTURA_AGENTE_CANONICOS

Use the creador-trello-editor-videos agent.

TAREA: crear UNA SOLA card en el board Trello editor de videos (board_id=rH2kazG2, lista "Input"/"Nueva tarea") con cover producto + z mp4 finalistas + uZ    briefs PDF (uno por finalista) + descripción con análisis ganador.

INPUT:
  producto: r  z
  flow_id: z7
  awareness_target (los 6 finalistas comparten este): z'
  deseo_principal (un dolor aislado): zI
  jerga_mass_education obligatoria en speech transmutado primeros 15s: "u"   "
  duración referente promedio: uN   s (los speech transmutados ±15% de esto, REGLA #32)

COMBO ganador completo:
Fre   r   z

FINALISTAS:
r  u   

REGLAS DURAS PARA LA CARD:
  R1) UNA SOLA card · NO POST duplicadas (si necesitas cambiar algo, PUT sobre la card_id existente).
  R2) name EXACTO: "u  "
  R3) Crear con pos="bottom" (abajo del todo lista Input · Fer msg 4868-4870).
  R4) Encoding UTF-8 nativo: ñ, á, é, ·, etc. correctos en name y desc.

REGLAS DURAS PARA CADA BRIEF PDF · ESTRUCTURA CANÓNICA 3 SECCIONES (REGLA #141 brief editor video, feedback_brief_editor_3_secciones.md). NO 6 PILARES, NO 6 SECCIONES — exactamente 3 con headings literales:

  ### Sección 1 — Video referencia
  Contenido obligatorio: URL FB Ads/GetHookd + advertiser + país (UK/USA/AU) + scaled_days (30-100) + evidencia escalado (Winning/Scaling/Growing GetHookd o spend_range) + awareness observado + sofisticación + thumbnail screenshot frame 0:01 embebido + destination_url del ad + mp4_path local en disco + nombre archivo mp4 adjunto en card (V1_xxx.mp4 / V2_xxx.mp4 / etc).

  ### Sección 2 — Explicación
  3-5 párrafos explicando: por qué este ref para nuestro producto + match awareness combo→ref + match cluster driver del combo (autoridad/reframe/demo/anti-fracaso) + transmutación necesaria al producto del input (qué partes mantener literal del ref + qué adaptar al mecanismo primario nuestro + jerga ES neutro peninsular) + validador externo (review Amazon o profesional médico citado).

  ### Sección 3 — Speech (prosa continua)
  Texto CONTINUO en párrafo único o 2-3 párrafos cortos. PROHIBIDO bullets, numeración, timestamps (1:23), notas en corchetes, instrucciones al editor, placeholders [IMG]. Estructura natural en prosa:
    - ABRE LITERAL con las 13 palabras del Hook 1 NATIVO adaptado al avatar ES (REGLA #28 hook nativo del ganador, cero precio cero producto literal, tono neutro peninsular REGLA #61: PROHIBIDO macho/flipé/joder/cabronazo/currar).
    - Beat problema con voz nativa peninsular (1-2 frases). La jerga mass education "u  " debe aparecer en los primeros 15 segundos del speech (≈37 primeras palabras · REGLA #112).
    - Mecanismo primario del producto explicado en 2-3 frases (NO copia literal del referente — transmutación al producto nuestro manteniendo awareness + deseo + estructura del ref).
    - 1-2 testimonios reales en PROSA tipo "como dice Carmen P de Madrid: ..." (NO formato "review #3 dice...").
    - Garantía 90 días devolución sin preguntas.
    - COD canónico "pagas en la puerta de tu casa" (NO "no pagas hasta que lo pruebes" · REGLA #63).
    - CTA cierre suave invitando a probar.
  CERO mención de precio numérico (no "34.95€", no "50% off") · el precio va en page producto/landing.
  Longitud target ±15% del speech original transcrito del ref ganador (REGLA #32).
  ES neutro peninsular sin mexicanismos/argentinismos (REGLA #61 sniff test lectura voz alta).

  Mínimo 60KB por PDF (no light). Sin URLs externas más allá de la Sección 1 ref (REGLA #45).

  Además de los 3 hooks que abren la sección 3 (Hook 1 nativo + 2 curiosity loops · REGLA #28), incluir al final de la Sección 3 un sub-bloque "## 3 HOOKS ALTERNATIVOS" con los 3 hooks listados (12-13 palabras cada uno, uno marcado [USAR COMO APERTURA]) para que el editor humano pueda elegir variante en post-producción.

Devuelve SOLO JSON sin texto: {"card_id":"...","short_url":"...","attachments_count":N,"awareness_target":"z"}r  r~  rv  u/   creador-trello-editor-videos · card creada · r  r{  u   card creada · )ra   r)   r^   rG  ro   rr   r   r   strftimerA   r6   r[   rp  )r   r   r   r  r   r  r  r  r  r  deseo_targetmass_educationduracion_reffecha	card_namer   r  r   r  s                      r   run_trello_editorr    s!    ""<06BJ{B/I
..


}b
1C}-3Ffbcfgpcqbrr}~?&=!$Y&4&<-
 !? 	 	A 5 AO+N+;<TVM!~~&89XVZZTW=X::/4LZZ 6s;N::6;LLLN##J/E&swwy'D&EQswwyY]G^F_  `D  EJ  DK  LI= >A^<LL^y>
 wwy,- .GGId+, -ll^ $AAQ@R S11= ?SSaRb c++7. 9$$(JJvE$J5D$Q#R S

95A%4HI J  )k *`( ao_o pw xH  wI  IL/k5	 p --KVUdpstOBcz	)FvzzR]G^F_`a )
)*N,Bd);  = ?6::k#:";<<<r   r   c                D    d   t         j                        sdd fS t              j                  di       j                  dd       j                  dg       D cg c]$  }|j                  d|j                  dd	            & }}d
j                  |      xs dd1 fd} |d      \  }}|rdd| fS |}	|r|j	                  dd d|	 d       t         |	      }
g }|
D ]  }|r,|j	                  dd|j                   d|j                           ||j                        \  }}|j                  |j                         |rdnd|d       |rp j                  d d|j                         |	|g dt               ddd       |r"|j	                  d d!|j                   d"|        dd#|j                   d| fc S |s|j	                  dd$|j                   d%|         |r|j	                  dd d&       t         |	|      }g }|D ]  }|r,|j	                  dd'|j                   d|j                           ||j                        \  }}|j                  |j                         |rdnd|d       |rp j                  d d(|j                         |	||dt               ddd       |r"|j	                  d d)|j                   d"|        dd*|j                   d| fc S |s|j	                  dd+|j                   d%|         |r|j	                  d,d- d.       t         |	||      } j                  d d/d/|	||t        |      t               ddd       dd0| fS c c}w )2u  Pipeline canónico de una estación con rescue ladder de Fer (msg 4789):

      1) Intento estándar (executor + supervisor.audit)
      2) Si falla → QC genera 3 hipótesis raíz distintas y se prueban en SERIE
      3) Si las 3 fallan → MASTER genera otras 3 hipótesis distintas y se prueban en SERIE
      4) Si las 3 del master fallan → ping Telegram a Fer pidiendo ayuda

    Devuelve (ok, msg) y deja en la caja la sección `_rescue_<sid>` con
    el dossier completo de qué se probó (para el master/forense).r   Fzno executor para r   r   zsupervisor-r  r   agent/agentec                ^    
|       \  }}|s1t        d d d|d d  d       t        d d d       dd| fS t        d	 d d
	 d       j                        \  }}|dk(  rt        d d d| d       dd| fS t        d d d| d| d       dd| d| fS )Nr  r  u   : «Jefe, fallé. r     »u2   : «Recibido. Pasamos a hipótesis alternativas.»Fu   EXEC_FAIL · rI  u   : «Hola jefe, ya terminé u   . ¿Está bien? Mira el log.»r   rv  u   : «Revisado. Todo OK (u!   ). Aprobado, pasa al siguiente.»Tu   EXEC_OK · SUP_OK · u   : «No me cuadra: r  u   . Tienes que corregir.»u   SUP_BREAK · )rG  r   )r  r  exec_msgverdictreasonagent_labelr   r   executorr   r   sup_idr   s        r   _attemptz(run_with_rescue_ladder.<locals>._attemptk  s   k7NKH&[Mvh6HRVSVHXXZ[\&VHE+6hijM(444fU6(2McURpqr$**40i)xu[M9PQWPXXyz{0999fk]2DWITRXQYYqrsgYd6(;;;r   r   Tu   std · r  u   ⚠ u    estándar FAIL · u'    · escalando a QC (3 hipótesis raíz)rI  u   🧪 QC r  PASSFAIL)r  r   r  _rescue_r   )level_resolvedwinning_hypothesisr   r  r,  
fer_pingedr.   r*   zrescue-ladderrz  rv  u   ✓ QC u	    PASS · r   u   ✗ QC 	    FAIL · u5    las 3 QC fallaron · escalando a MASTER (QC central)u   🧪 MASTER r   u   ✓ MASTER r   u   ✗ MASTER rt     ❌ u9    3+3 hipótesis FAIL · pidiendo ayuda a Fer por TelegramNu&   rescue_ladder_exhausted · fer_pinged=)r  rw   ry   tuple[bool, str])	EXECUTORSr)   r   r  rE  r   r   r   r   rc   r   rr   r   r  r6  r   )r   r   r   bridger	  agent_namesr  r  rh  r   qc_hypsr  r   master_hypsr,  pingedr  r  r   r  r   s   ```             @@@@@r   run_with_rescue_ladderr
  U  s    d
C}}S!H)#///K9J__\2.224;se9LMF?JxY[?\]?\!155tG!45?\K]((;'38K< <" rlGB	wse_$$M&D-@Ov"wx '{D-QGKfd166(&CD1::&C))+ "f
 	
 #/"&&'iik!.*#%#i2 _  6 !!)wqttfIcU-KL3qttfD...fvYse&DE/ 4 &D-b"cd.{D-Q\^efKOfQTTF$qvvh&GH1::&C))+ "f 
 	
 #/"*&'iik!.*#2#i2 _  6 !!){144&	#-OP7144&SE222fADD63%&HI/ 4 'T#.g#hiT=+`ghF#'"&"*6li* _  . :6(CCCE ^s   2)Nc                     t        | |||      S )Nr  )r
  )r   r   r   max_retriesr  s        r   run_with_supervisorr    s     "$WVLLr   c                  P    e Zd ZdZddZddZddZddZddZddZ	dddZ
d	 Zy
)TaskStateBridgezEMantiene _task_state.json sincronizado con el progreso real del flow.c                X    t         | _        || _        || _        | j	                  |       y r   )
STATE_PATHrG   rA   station_ids_init_state)rK   rA   payloadr  s       r   rN   zTaskStateBridge.__init__  s&    	&!r   c                    | j                   || j                  ddg dd g g ddddt        t        j                         dz        d}| j	                  |       y )Nr   
processingr   )rA   rQ  routerouteIdxstaterE   transition_trv  demo_logs_completedlogsstarted_at_flow_sstarted_at_station_selapsed_at_flow_selapsed_at_station_ssaved_at_wall)rA   r  r   r   rJ   )rK   r  r  s      r   r  zTaskStateBridge._init_state  s`     ||%%!#%!"$%!"$% t!34
" 	

5r   c                    | j                   j                         r0	 t        j                  | j                   j	                  d            S i S # t
        $ r Y i S w xY wr2   )rG   rI   r6   r7   r9   r   rQ   s    r   rR   zTaskStateBridge._load  sX    99zz$))"5"5w"5"GHH 	  	s   .A 	AAc                   t        t        j                         dz        |d<   	 | j                  j                  t	        j
                  |dd      d       y # t        $ r(}t        d| t        j                  	       Y d }~y d }~ww xY w)
Nr   r"  Fr	   rV   r3   r4   z5[bridge] WARN: no se pudo escribir _task_state.json: file)
r   r   rG   rZ   r6   r[   r   rF  sysrJ  )rK   r  r5  s      r   rJ   zTaskStateBridge._save  su    !$TYY[4%7!8o	`II  Ea!P[b c 	`I!MTWT^T^__	`s   3A 	B
"BB
c                   | j                         }|sy |j                  dg       j                  t               ||d       |d   dd  |d<   | j	                  |       	 t        d|j                          d|        y # t        $ r Y y w xY w)Nr  )r.   r   r   irC  rD  )rR   rj   rc   r   rJ   rF  r   r   )rK   r   r   r    s       r   rE  zTaskStateBridge.append_log  s    JJL	VR ''))
 	 fIef%&	

1	Aekkm_Btf-. 		s   A> >	B
	B
c                    | j                         }|sy ||d<   d|d<   d|d<   d|d<   t        t        j                         dz        |d<   | j                  |       y )	Nr  r  r  r   r  r  r   r"  )rR   r   r   rJ   )rK   	route_idxr    s      r   advance_stationzTaskStateBridge.advance_station   s_    JJL!*!'
.$%
 ! t!34/

1r   c                    |r| j                  |rdnd|       | j                         }|sy d|d<   ||d<   | j                  |       y )Nrv  rt  doner  )rE  rR   rJ   )rK   rv  summaryr    s       r   	mark_donezTaskStateBridge.mark_done+  sE    OOIgwGJJL'
)

1r   c                    | j                   j                         r	 | j                   j                          y y # t        $ r Y y w xY wr   )rG   rI   unlinkr   rQ   s    r   clearzTaskStateBridge.clear5  s@    99		  "   s   8 	AAN)rA   rw   r  rm   r  r   )r  rm   rx   )r  rm   r   rw   r   rw   )r*  r   r   )rv  r   r.  rw   )rz   r{   r|   r}   rN   r  rR   rJ   rE  r+  r/  r2  r   r   r   r  r    s.    O"*`*	r   r  c           
     R   t        j                  | j                  d            }t        ||       }t        |z  }|j                  dd       t        |||      }t               }|d   }|rt        |||D 	cg c]  }	|	d   	 c}	      nd }
t        |
       g }|j                  d| d| d	t        |              |
r|
j                  d
d| d| d       t        |      D ]  \  }}|d   }|j                  dg       D cg c]  }|d   	 }}|j                  d| d|        |
r|
j                  |       |
j                  d
d|j!                          ddj#                  |              |j                  di       j                  dd      }|
j                  d
d| d|j!                          d       t%        ||||
      \  }}|su|j                  d| d|        |
r>|
j                  dd|j!                          d|        |
j'                  d d!| d"|        d#||||t)        |j*                        d$c S |j                  d%| d|        |
sz|
j                  d&d'|j!                          d(|         |j                  d)| d*|j*                   d+|j-                          d,       |
rK|
j                  d&d-|j-                          d,       |
j'                  dd| d.|j-                          d,       d/||t)        |j*                        d0S c c}	w c c}w )1Nr3   r4   Tr>   stationsr   z[start] flow_id=u    · dry_run=u    · stations=rI  zFlow u    arrancado · dry_run=u    · 6 estacionesr  r   z
[station] u    · agentes=u   ▶ Entrando estación u    · agentes: z, r   r   u   👁 Supervisor u    en posición · base PDF: PDF_z.pdfr  z[ABORT] r  rt  r  r  FzFlow abortado en z: r  )r+  rA   stationr  log	caja_pathz[ok] rv  u   ✓ u    OK · z[done] flow u    cerrado · caja=u	    · size=r>  u-   🎯 Flow completado · 6 estaciones · caja u    OK · caja OK)r+  rA   r8  r9  )r6   r7   r9   r0   r  rH   r<   r:   r  r?  rc   ro   rE  r   r)   r+  r   r  r
  r/  rw   rG   rv   )r-   r   bridge_enabledr  rA   rF   r   metar6  r    r  r8  idxr7  r   r	  r  r  r  rh  s                       r   run_flowr>  =  sJ   jj--w-?@GWj1G GMM$M.'*D;DJHO]_Wg/JA$/JKcgFv
CJJ!',wi}SQY]O\]&E'2H	Qa"bc!(+Wdm*1++h*CD*CQqy*CD

ZuL>?""3'f(?		}MZ^ZcZcdoZpYq&rs[[r266t]KFf(8@_`c`i`i`k_llp&qr(wOCJJ#d3%01!!'T#))+iu+MN  *;C53%(HI$STW`cruvzvv  sA  B  B

U3%tC5)*i4		}GC5)IJ) ,, JJgY&7		{)DOOL]K^^_`a)'TUYUdUdUfTggh%ijwi|DOO<M;NaPQwsTYYXXE 0K Es   7LL$c                    t        j                         } | j                  ddd       | j                  dt        d       | j                  ddd	       | j	                         }|j
                  rt        |j
                        }|j                         s	t        |z  }|j                         s3t        d
| t        j                         t        j                  d       t        ||j                        }t        t!        j"                  |dd             t        j                  |d   dk(  rdnd       t        dt$         dt&         d|j                          	 t(        j                         rt        d       y t+        t$        j-                  d            D ]  }|j/                  d      }|j                         r%t        d|j0                          t        ||j                        }|j3                  t!        j"                  |dd      d       t        d|j0                   d|d           |j4                  s y  t7        j8                  t&               )Nz	--dry-run
store_truez$Smoke test sin red/LLM/Trello reales)actionhelpz--from-inboxz*Ruta a inbox JSON (sino, watcher infinito))rL  rB  z--oncezEjecuta un solo flow y salezERROR: inbox file no existe: r%  r	   r   FrV   r+  r:  r   r   z[watcher] poll z cada u   s · dry_run=u%   [watcher] stop flag detectado · exitzflow_*.jsonz.donez[watcher] procesando r3   r4   z
[watcher] r  )argparseArgumentParseradd_argumentrw   
parse_args
from_inboxr   is_absoluter   rI   rF  r'  rJ  exitr>  r   r6   r[   	INBOX_DIRPOLL_INTERVAL_S	STOP_FLAGsortedglobwith_suffixr   rZ   oncer   sleep)r  argsr-   r   fdone_markers         r   mainrU  p  s   !ANN;|:`NaNN>2^N_NN8L7TNU<<>D$//*
%%'
*J  "1*>SZZPHHQK*dll;djjeA>?fX&$.A6 
OI;f_,=]4<<.
YZ
9:	}56A--0K!!#)!&&23a6F""4::f5QR#S^e"fJqvvheF8,<+=>?yy 7 	

?# r   __main__r3   r4   )r    rw   ry   rw   r   )r,   rm   r-   zPath | Nonery   rw   rx   )r  )r   rw   r   rw   r   rw   r   rw   r   r   ry   zlist[Hypothesis] | None)
r   rm   r   'Caja'r   rw   r   r   ry   list[Hypothesis])r   rm   r   rW  r   rw   r  rl   r   r   ry   rX  )r   rm   r   rW  r   rw   r  rl   r,  rl   r   r   ry   r   )r8  z
str | Nonery   rw   r3  )r}  )
r`  rw   r   rw   r   rw   r   r   ry   ztuple[bool, dict | None, str]r4  )r   r<   r   rm   r   r   r  rw   )
r   r<   r   rm   r   r   r  r:  ry   r  )r   r<   r   rm   r   r   r  r   r  r:  ry   r  )FT)r-   r   r   r   r;  r   ry   rm   )=r}   
__future__r   rC  r6   osr'  r   r   r   pathlibr   r   r   __file__r   r   r8   rJ  r  r  rL  rK  DEFAULT_MAX_RETRIESr)  r&  r   r   r!   r0   r:   r<   r   r   r   r   r   r  r6  r9  r;  __annotations__r?  rA  rG  rp  r  r  r  r  r  r  r  r
  r  r  r>  rU  rz   r   reconfigurer   r   r   <module>r`     sT  !D #   	 
   	  'H~**	= 	'>&&
,,	  F   2#=<E <EF+ +d3- 3-D
b 
b NQ"7:"GJ"Ul"J<~8/38>B8GW8v3%)3<@3KO3TX3tf ,0( /
' .1m?'*m?5Rm?`=K~(GTE8NvUp9Uv\=~ " &	 @DuD$<uDHXuDt ,?;?M%(M 8MDTM \ \~,Yf"$J zJJG,F r   