§
    À5Çj^›  ã                   ó  — d Z ddlZddlmc 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mZ  ee¦  «                             ¦   «         j        Zej        Z ee¦  «        ej        vr$ej                             d ee¦  «        ¦  «          ee¦  «        ej        vr$ej                             d ee¦  «        ¦  «         ddlmZmZmZmZmZmZmZmZmZm Z m!Z!m"Z"  G d„ dej#        ¦  «        Z$e%dk    r ej&        ¦   «          dS dS )z…
Tier 1: Feature Coverage Test Suite (F1 - F13)
Contains >= 5 distinct, authoritative test cases per feature (Total: 65 test cases).
é    N)ÚPath)ÚFILMMAKER_DIRÚPROMPTS_DIRÚ
ASSETS_DIRÚ	BIBLE_DIRÚPIPELINE_DIRÚEXPORTS_DIRÚWEB_REVIEW_DIRÚread_ep01_screenplayÚget_screenplay_scenesÚfind_male_crying_violationsÚload_muse_prompts_dataÚload_banana_prompts_datac                   ó˜  — e Zd ZdZd„ Zd„ Zd„ Zd„ Zd„ Zd„ Z	d„ Z
d	„ Zd
„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Z d„ Z!d „ Z"d!„ Z#d"„ Z$d#„ Z%d$„ Z&d%„ Z'd&„ Z(d'„ Z)d(„ Z*d)„ Z+d*„ Z,d+„ Z-d,„ Z.d-„ Z/d.„ Z0d/„ Z1d0„ Z2d1„ Z3d2„ Z4d3„ Z5d4„ Z6d5„ Z7d6„ Z8d7„ Z9d8„ Z:d9„ Z;d:„ Z<d;„ Z=d<„ Z>d=„ Z?d>„ Z@d?„ ZAd@„ ZBdA„ ZCdB„ ZDdCS )DÚTestTier1FeatureszS
    Tier 1 tests verify the primary requirement contracts for F1 through F13.
    c                 ó–  — t          ¦   «         }|                      t          |¦  «        dk    d¦  «         t          j        d|t          j        ¦  «        }t          ¦   «         }d„ |                     di ¦  «        D ¦   «         }t          t          |¦  «        t          |¦  «        ¦  «        }|  	                    |dd|› d�¦  «         d	S )
zFF1.1: Verify EP01 screenplay defines >= 150 shots (target: 188 shots).r   z(TAP_01 screenplay file must not be emptyu6   (?:shot\s*(\d+)|cáº£nh\s*\d+[^:\n]*\bshot\b|\bSH_?\d+)c                 ó>   — g | ]}d |                      ¦   «         v ¯|‘ŒS ©Úep01©Úlower©Ú.0Úks     ú2C:\Projects\KieuStory\tests\test_tier1_features.pyú
<listcomp>zHTestTier1Features.test_f1_01_ep01_shot_count_minimum.<locals>.<listcomp>+   s/   € Ð^Ð^Ð^˜aÈ&ÐTU×T[ÒT[ÑT]ÔT]ÐJ]ÐJ]˜ÐJ]ÐJ]ÐJ]ó    Úmotion_promptsé–   zEP01 total shot count (z+) must be >= 150 for 25-30m feature length.N)
r   Ú
assertTrueÚlenÚreÚfindallÚ
IGNORECASEr   ÚgetÚmaxÚassertGreaterEqual)ÚselfÚ
screenplayÚshot_matchesÚ	muse_dataÚep01_promptsÚeffective_counts         r   Ú"test_f1_01_ep01_shot_count_minimumz4TestTier1Features.test_f1_01_ep01_shot_count_minimum$   sÇ   € å)Ñ+Ô+ˆ
Ø�Š�˜J™œ¨!Ò+Ð-WÑXÔXÐXÝ”zÐ"[Ð]gÕikÔivÑwÔwˆå*Ñ,Ô,ˆ	Ø^Ð^ 9§=¢=Ð1AÀ2Ñ#FÔ#FÐ^Ñ^Ô^ˆÝ�c ,Ñ/Ô/µ°\Ñ1BÔ1BÑCÔCˆØ×ÒØ˜SØb oÐbÐbÐbñ	
ô 	
ð 	
ð 	
ð 	
r   c                 ó¼   — t          ¦   «         }d„ |                     di ¦  «        D ¦   «         }t          |¦  «        dz  }|                      |dd|› d�¦  «         dS )zNF1.2: Verify calculated total runtime of EP01 is >= 1500 seconds (25 minutes).c                 ó>   — g | ]}d |                      ¦   «         v ¯|‘ŒS r   r   r   s     r   r   zDTestTier1Features.test_f1_02_ep01_total_duration.<locals>.<listcomp>5   s/   € Ð\Ð\Ð\˜AÈÐRS×RYÒRYÑR[ÔR[ÐH[ÐH[�aÐH[ÐH[ÐH[r   r   é
   éÜ  zEP01 total runtime (z!s) must be >= 1500s (25 minutes).N)r   r%   r!   r'   )r(   r+   Ú
ep01_shotsÚtotal_duration_secs       r   Útest_f1_02_ep01_total_durationz0TestTier1Features.test_f1_02_ep01_total_duration2   sv   € å*Ñ,Ô,ˆ	Ø\Ð\ §¢Ð/?ÀÑ!DÔ!DÐ\Ñ\Ô\ˆ
Ý  ™_œ_¨rÑ1ÐØ×ÒØ ØXÐ#5ÐXÐXÐXñ	
ô 	
ð 	
ð 	
ð 	
r   c                 ó´  — t          ¦   «         }t          t          j        d|t          j        ¦  «        ¦  «        }t          t          j        d|t          j        ¦  «        ¦  «        }t          t          j        d|t          j        ¦  «        ¦  «        }|                      |d¦  «         |                      |d¦  «         |                      |d¦  «         dS )zGF1.3: Verify clear 3-Act classical cinema structure in EP01 screenplay.u   Há»’I\s*1\bu   Há»’I\s*2\bu   Há»’I\s*3\bu,   EP01 screenplay must contain Act 1 (Há»’I 1)u,   EP01 screenplay must contain Act 2 (Há»’I 2)u,   EP01 screenplay must contain Act 3 (Há»’I 3)N©r   Úboolr"   Úsearchr$   r    )r(   r)   Ú	has_act_1Ú	has_act_2Ú	has_act_3s        r   Ú,test_f1_03_ep01_three_act_structure_presencez>TestTier1Features.test_f1_03_ep01_three_act_structure_presence<   s©   € å)Ñ+Ô+ˆ
Ý�œ >°:½r¼}ÑMÔMÑNÔNˆ	Ý�œ >°:½r¼}ÑMÔMÑNÔNˆ	Ý�œ >°:½r¼}ÑMÔMÑNÔNˆ	Ø�Š˜	Ð#QÑRÔRÐRØ�Š˜	Ð#QÑRÔRÐRØ�Š˜	Ð#QÑRÔRÐRÐRÐRr   c                 óä  — t          ¦   «         }t          |¦  «        }|                      t          |¦  «        dd¦  «         g }|D ]\\  }}t	          j        d|t          j        ¦  «        }|r5|                     t          | 	                    d¦  «        ¦  «        ¦  «         Œ]t          dt          t          |¦  «        dz   d¦  «        ¦  «        D ]}|                      ||d|d›d�¦  «         Œd	S )
zIF1.4: Verify sequential scene indexing from Scene 01 up through Scene 15.é   z*EP01 must have at least 12 distinct scenesu   Cáº¢NH\s*0?(\d+)é   é   zMissing Scene Ú02dz in EP01 screenplayN)r   r   r'   r!   r"   r9   r$   ÚappendÚintÚgroupÚrangeÚminÚassertIn)r(   r)   ÚscenesÚscene_indicesÚheaderÚ_ÚmÚexpecteds           r   Ú)test_f1_04_ep01_scene_continuity_indexingz;TestTier1Features.test_f1_04_ep01_scene_continuity_indexingF   sú   € å)Ñ+Ô+ˆ
Ý& zÑ2Ô2ˆØ×Ò¥ F¡¤¨RÐ1]Ñ^Ô^Ð^ØˆØð 	6ð 	6‰IˆF�AÝ”	Ð-¨vµr´}ÑEÔEˆAØð 6Ø×$Ò$¥S¨¯ª°©¬¡_¤_Ñ5Ô5Ð5øÝ˜a¥¥S¨Ñ%7Ô%7¸!Ñ%;¸RÑ!@Ô!@ÑAÔAð 	gð 	gˆHØ�MŠM˜( MÐ3eÀHÐ3eÐ3eÐ3eÐ3eÑfÔfÐfÐfð	gð 	gr   c                 óþ  — t          ¦   «         }t          j        d|t          j        t          j        z  ¦  «        }t          j        d|t          j        t          j        z  ¦  «        }t          j        d|t          j        t          j        z  ¦  «        }|                      |d¦  «         |                      |d¦  «         |                      |d¦  «         |                      t          |                     d¦  «        ¦  «        dd	¦  «         |                      t          |                     d¦  «        ¦  «        dd
¦  «         |                      t          |                     d¦  «        ¦  «        dd¦  «         dS )zdF1.5: Verify balanced pacing distribution across acts (Act 1 >= 500s, Act 2 >= 500s, Act 3 >= 350s).u   Há»’I\s*1.*?(?=Há»’I\s*2|\Z)u   Há»’I\s*2.*?(?=Há»’I\s*3|\Z)u   Há»’I\s*3.*?(?=\Z)zAct 1 must be delineablezAct 2 must be delineablezAct 3 must be delineabler   r2   z#Act 1 content must be comprehensivez#Act 2 content must be comprehensivez#Act 3 content must be comprehensiveN)	r   r"   r9   ÚDOTALLr$   ÚassertIsNotNoneÚassertGreaterr!   rE   )r(   r)   Ú
act1_matchÚ
act2_matchÚ
act3_matchs        r   Ú0test_f1_05_ep01_pacing_act_duration_distributionzBTestTier1Features.test_f1_05_ep01_pacing_act_duration_distributionS   s@  € å)Ñ+Ô+ˆ
Ý”YÐ>À
ÍBÌIÕXZÔXeÑLeÑfÔfˆ
Ý”YÐ>À
ÍBÌIÕXZÔXeÑLeÑfÔfˆ
Ý”YÐ4°jÅ"Ä)ÍbÌmÑB[Ñ\Ô\ˆ
Ø×Ò˜ZÐ)CÑDÔDÐDØ×Ò˜ZÐ)CÑDÔDÐDØ×Ò˜ZÐ)CÑDÔDÐDà×Ò�3˜z×/Ò/°Ñ2Ô2Ñ3Ô3°TÐ;`ÑaÔaÐaØ×Ò�3˜z×/Ò/°Ñ2Ô2Ñ3Ô3°TÐ;`ÑaÔaÐaØ×Ò�3˜z×/Ò/°Ñ2Ô2Ñ3Ô3°TÐ;`ÑaÔaÐaÐaÐar   c                 ó@  — t          ¦   «         }t          |¦  «        }d}|D ]\  }}d|v sd|v r|} nŒ|                      t          |¦  «        dk    d¦  «         t	          t          j        d|t
          j        ¦  «        ¦  «        }|                      |d¦  «         dS )	uT   F2.1: Verify Scene 05 contains Beat A (Stroll along 'tiá»ƒu khÃª' / winding stream).Ú õ	   Cáº¢NH 05õ   Cáº¢NH 5r   z!Scene 05 must exist in screenplayuF   (tiá»ƒu khÃª|suá»‘i nhá»�|bÆ°á»›c dáº§n theo ngá»�n|láº§n theo ngá»�n)uH   Scene 05 Beat A must feature stroll along 'tiá»ƒu khÃª' / winding streamN)r   r   r    r!   r8   r"   r9   r$   )r(   r)   rI   Úscene05_textÚhÚcÚhas_tieu_khes          r   Ú$test_f2_01_c05_a_stroll_along_streamz6TestTier1Features.test_f2_01_c05_a_stroll_along_streamd   sÈ   € å)Ñ+Ô+ˆ
Ý& zÑ2Ô2ˆØˆØð 	ð 	‰DˆAˆqØ˜aÐÐ :° ? ?Ø �Ø�ð $3ð 	�Š�˜LÑ)Ô)¨AÒ-Ð/RÑSÔSÐSÝ�BœIÐ&oÐq}õ  @Bô  @Mñ  Nô  Nñ  Oô  OˆØ�Š˜Ð&pÑqÔqÐqÐqÐqr   c                 óŠ  — t          ¦   «         }t          |¦  «        }d                     d„ |D ¦   «         ¦  «        }t          t	          j        d|t          j        ¦  «        ¦  «        }t          t	          j        d|t          j        ¦  «        ¦  «        }|                      |d¦  «         |                      |d¦  «         dS )uU   F2.2: Verify Scene 05 contains Beat B (Inquiry & wonder at deserted mound 'sÃ¨ sÃ¨').rY   c              3   ó0   K  — | ]\  }}d |v sd|v ¯|V — ŒdS ©rZ   r[   N© ©r   r]   r^   s      r   ú	<genexpr>zFTestTier1Features.test_f2_02_c05_b_inquiry_at_mound.<locals>.<genexpr>u   ó5   è è € Ð[Ð[¡T Q¨°{ÀaÐ7GÐ7GÈ:ÐYZÈ?È?˜qÈ?È?È?È?Ð[Ð[r   u!   (sÃ¨ sÃ¨|náº¥m má»“|náº¥m Ä‘áº¥t)uC   (hÆ°Æ¡ng khÃ³i váº¯ng tanh|sao trong tiáº¿t thanh minh|váº¯ng tanh)u9   Scene 05 Beat B must feature the deserted mound 'sÃ¨ sÃ¨'u?   Scene 05 Beat B must feature Kiá»�u's inquiry at deserted moundN©r   r   Újoinr8   r"   r9   r$   r    )r(   r)   rI   r\   Ú	has_se_seÚhas_inquirys         r   Ú!test_f2_02_c05_b_inquiry_at_moundz3TestTier1Features.test_f2_02_c05_b_inquiry_at_moundq   s»   € å)Ñ+Ô+ˆ
Ý& zÑ2Ô2ˆØ—w’wÐ[Ð[¨VÐ[Ñ[Ô[Ñ[Ô[ˆÝ�œÐ#GÈÕWYÔWdÑeÔeÑfÔfˆ	Ý�2œ9Ð%kÐmyÕ{}ô  |Iñ  Jô  Jñ  Kô  KˆØ�Š˜	Ð#^Ñ_Ô_Ð_Ø�Š˜Ð%fÑgÔgÐgÐgÐgr   c                 óŠ  — t          ¦   «         }t          |¦  «        }d                     d„ |D ¦   «         ¦  «        }t          t	          j        d|t          j        ¦  «        ¦  «        }t          t	          j        d|t          j        ¦  «        ¦  «        }|                      |d¦  «         |                      |d¦  «         dS )ub   F2.3: Verify Scene 05 contains Beat C (Emotional grief for women's fate 'báº¡c má»‡nh' and tears).rY   c              3   ó0   K  — | ]\  }}d |v sd|v ¯|V — ŒdS rc   rd   re   s      r   rf   zETestTier1Features.test_f2_03_c05_c_grief_and_tears.<locals>.<genexpr>   rg   r   uF   (Ä‘au Ä‘á»›n thay pháº­n Ä‘Ã n bÃ |báº¡c má»‡nh cÅ©ng lÃ  lá»�i chung)u(   (nÆ°á»›c máº¯t|rÆ¡i lá»‡|chÃ¢u sa|khÃ³c)uK   Scene 05 Beat C must contain iconic grief for women's fate ('báº¡c má»‡nh')uB   Scene 05 Beat C must contain ThÃºy Kiá»�u weeping for Ä�áº¡m TiÃªnNrh   )r(   r)   rI   r\   Ú	has_griefÚhas_kieu_tearss         r   Ú test_f2_03_c05_c_grief_and_tearsz2TestTier1Features.test_f2_03_c05_c_grief_and_tears{   s¼   € å)Ñ+Ô+ˆ
Ý& zÑ2Ô2ˆØ—w’wÐ[Ð[¨VÐ[Ñ[Ô[Ñ[Ô[ˆÝ�œÐ#lÐnzÕ|~ô  }Jñ  Kô  Kñ  Lô  Lˆ	Ý�bœiÐ(SÐUaÕceÔcpÑqÔqÑrÔrˆØ�Š˜	Ð#pÑqÔqÐqØ�Š˜Ð(lÑmÔmÐmÐmÐmr   c                 ó  — t          ¦   «         }t          |¦  «        }d                     d„ |D ¦   «         ¦  «        }t          t	          j        d|t          j        ¦  «        ¦  «        }|                      |d¦  «         dS )uo   F2.4: Verify Scene 05 contains Beat C supernatural footprints in moss ('dáº¥u hÃ i' / 'dáº¥u giÃ y' 'in rÃªu').rY   c              3   ó0   K  — | ]\  }}d |v sd|v ¯|V — ŒdS rc   rd   re   s      r   rf   zMTestTier1Features.test_f2_04_c05_c_supernatural_footprints.<locals>.<genexpr>‰   rg   r   u2   (dáº¥u hÃ i|dáº¥u giÃ y|in rÃªu|dáº¥u chÃ¢n.*rÃªu)uL   Scene 05 must contain supernatural footprints in moss ('dáº¥u hÃ i in rÃªu')Nrh   )r(   r)   rI   r\   Úhas_footprintss        r   Ú(test_f2_04_c05_c_supernatural_footprintsz:TestTier1Features.test_f2_04_c05_c_supernatural_footprints…   sv   € å)Ñ+Ô+ˆ
Ý& zÑ2Ô2ˆØ—w’wÐ[Ð[¨VÐ[Ñ[Ô[Ñ[Ô[ˆÝ�bœiÐ(]Ð_kÕmoÔmzÑ{Ô{Ñ|Ô|ˆØ�Š˜Ð(vÑwÔwÐwÐwÐwr   c                 ó®  — t          ¦   «         }t          |¦  «        }d                     d„ |D ¦   «         ¦  «        }t          j        d|t          j        ¦  «        }t          j        d|t          j        ¦  «        }t          j        d|t          j        ¦  «        }|                      |d¦  «         |                      |d¦  «         |                      |d¦  «         |                      |                     ¦   «         |                     ¦   «         d	¦  «         |                      |                     ¦   «         |                     ¦   «         d
¦  «         dS )z_F2.5: Verify strict temporal order: Stroll (A) -> Wonder/Inquiry (B) -> Grief & Footprints (C).rY   c              3   ó0   K  — | ]\  }}d |v sd|v ¯|V — ŒdS rc   rd   re   s      r   rf   zITestTier1Features.test_f2_05_scene05_sub_beat_ordering.<locals>.<genexpr>‘   rg   r   u"   (tiá»ƒu khÃª|bÆ°á»›c dáº§n|suá»‘i)u    (sÃ¨ sÃ¨|náº¥m má»“|váº¯ng tanh)u6   (Ä‘au Ä‘á»›n thay|báº¡c má»‡nh|dáº¥u hÃ i|dáº¥u giÃ y)z%Beat A keyword must exist in Scene 05z%Beat B keyword must exist in Scene 05z%Beat C keyword must exist in Scene 05z-Beat A (stroll) must precede Beat B (inquiry)z,Beat B (inquiry) must precede Beat C (grief)N)	r   r   ri   r"   r9   r$   rR   Ú
assertLessÚstart)r(   r)   rI   r\   Úpos_aÚpos_bÚpos_cs          r   Ú$test_f2_05_scene05_sub_beat_orderingz6TestTier1Features.test_f2_05_scene05_sub_beat_ordering�   s  € å)Ñ+Ô+ˆ
Ý& zÑ2Ô2ˆØ—w’wÐ[Ð[¨VÐ[Ñ[Ô[Ñ[Ô[ˆÝ”	Ð?ÀÍrÌ}Ñ]Ô]ˆÝ”	Ð=¸|ÍRÌ]Ñ[Ô[ˆÝ”	ÐSÐUaÕceÔcpÑqÔqˆØ×Ò˜UÐ$KÑLÔLÐLØ×Ò˜UÐ$KÑLÔLÐLØ×Ò˜UÐ$KÑLÔLÐLØ�Š˜Ÿš™œ u§{¢{¡}¤}Ð6eÑfÔfÐfØ�Š˜Ÿš™œ u§{¢{¡}¤}Ð6dÑeÔeÐeÐeÐer   c                 óî   — t          ¦   «         }t          |¦  «        }d„ |D ¦   «         }|                      t          |¦  «        ddt          |¦  «        › d�d                     |¦  «        z   ¦  «         dS )zQF3.1: Zero male character crying cues across all action beats in EP01 screenplay.c           	      óJ   — g | ] }d |d         › d|d         › d|d         › d�‘Œ!S )úLine Úline_numz: Ú	characterz -> 'Útextú'rd   )r   Úvs     r   r   zRTestTier1Features.test_f3_01_no_male_crying_in_ep01_screenplay.<locals>.<listcomp>¢   sA   € ÐmÐmÐmÐ]^ÐX a¨
¤mÐXÐX°q¸´~ÐXÐXÈAÈfÌIÐXÐXÐXÐmÐmÐmr   r   z	Detected z% Anti-Male-Tears violations in EP01:
ú
N)r   r   ÚassertEqualr!   ri   )r(   r)   Ú
violationsÚviolation_messagess       r   Ú,test_f3_01_no_male_crying_in_ep01_screenplayz>TestTier1Features.test_f3_01_no_male_crying_in_ep01_screenplayž   s…   € å)Ñ+Ô+ˆ
Ý0°Ñ<Ô<ˆ
ØmÐmÐblÐmÑmÔmÐØ×ÒÝ�
‰OŒO˜QØO�˜J™œÐOÐOÐOÐRV×R[ÒR[Ð\nÑRoÔRoÑoñ	
ô 	
ð 	
ð 	
ð 	
r   c                 óì   ‡— t          ¦   «         }|                     ¦   «         }t          |d¦  «        D ]?\  }Šd‰v r6t          ˆfd„dD ¦   «         ¦  «        r|                      d|› d‰› �¦  «         Œ@dS )uI   F3.2: Specifically verify Kim Trá»�ng does NOT weep during parting scene.r@   u   KIM TRá»ŒNGc              3   óD   •K  — | ]}|‰                      ¦   «         v V — Œd S ©Nr   ©r   ÚwÚlines     €r   rf   zJTestTier1Features.test_f3_02_kim_trong_parting_stoicism.<locals>.<genexpr>­   sH   øè è € ð  -Lð  -LÀ1¨Q°$·*²*±,´,Ð->ð  -Lð  -Lð  -Lð  -Lð  -Lð  -Lr   )õ   khÃ³cõ	   rÆ¡i lá»‡õ   rÆ¡i nÆ°á»›c máº¯tu   ngháº¹n ngÃ o rÆ¡ir€   u.    violates Anti-Male-Tears: Kim Trá»�ng weeps: N©r   Ú
splitlinesÚ	enumerateÚanyÚfail©r(   r)   ÚlinesÚidxr�   s       @r   Ú%test_f3_02_kim_trong_parting_stoicismz7TestTier1Features.test_f3_02_kim_trong_parting_stoicism¨   s¼   ø€ å)Ñ+Ô+ˆ
Ø×%Ò%Ñ'Ô'ˆÝ" 5¨!Ñ,Ô,ð 	]ð 	]‰IˆC�Ø Ð$Ð$­ð  -Lð  -Lð  -Lð  -Lð  IKð  -Lñ  -Lô  -Lñ  *Lô  *LÐ$Ø—	’	Ð[ #Ð[Ð[ÐUYÐ[Ð[Ñ\Ô\Ð\øð	]ð 	]r   c                 óì   ‡— t          ¦   «         }|                     ¦   «         }t          |d¦  «        D ]?\  }Šd‰v r6t          ˆfd„dD ¦   «         ¦  «        r|                      d|› d‰› �¦  «         Œ@dS )u_   F3.3: Verify VÆ°Æ¡ng Quan maintains stoic composure during Ä�áº¡m TiÃªn tale and family crisis.r@   u   VÆ¯Æ NG QUANc              3   óD   •K  — | ]}|‰                      ¦   «         v V — Œd S r�   r   rŽ   s     €r   rf   zITestTier1Features.test_f3_03_vuong_quan_stoic_delivery.<locals>.<genexpr>µ   s0   øè è € Ð-vÐ-vÀA¨a°4·:²:±<´<Ð.?Ð-vÐ-vÐ-vÐ-vÐ-vÐ-vr   )r‘   r’   r“   r€   u/    violates Anti-Male-Tears: VÆ°Æ¡ng Quan weeps: Nr”   r™   s       @r   Ú$test_f3_03_vuong_quan_stoic_deliveryz6TestTier1Features.test_f3_03_vuong_quan_stoic_delivery°   s�   ø€ å)Ñ+Ô+ˆ
Ø×%Ò%Ñ'Ô'ˆÝ" 5¨!Ñ,Ô,ð 	^ð 	^‰IˆC�Ø Ð%Ð%­#Ð-vÐ-vÐ-vÐ-vÐIuÐ-vÑ-vÔ-vÑ*vÔ*vÐ%Ø—	’	Ð\ #Ð\Ð\ÐVZÐ\Ð\Ñ]Ô]Ð]øð	^ð 	^r   c                 óì   ‡— t          ¦   «         }|                     ¦   «         }t          |d¦  «        D ]?\  }Šd‰v r6t          ˆfd„dD ¦   «         ¦  «        r|                      d|› d‰› �¦  «         Œ@dS )u^   F3.4: Verify VÆ°Æ¡ng Ã”ng preserves paternal dignity during arrest without hysterical sobbing.r@   u   VÆ¯Æ NG Ã”NGc              3   óD   •K  — | ]}|‰                      ¦   «         v V — Œd S r�   r   rŽ   s     €r   rf   zOTestTier1Features.test_f3_04_vuong_ong_dignity_during_arrest.<locals>.<genexpr>½   s0   øè è € Ð-xÐ-xÀA¨a°4·:²:±<´<Ð.?Ð-xÐ-xÐ-xÐ-xÐ-xÐ-xr   )u   khÃ³c ngáº¥tu   khÃ³c rá»‘ngu
   gÃ o khÃ³cr€   u>    violates Anti-Male-Tears: VÆ°Æ¡ng Ã”ng breaks stoic dignity: Nr”   r™   s       @r   Ú*test_f3_04_vuong_ong_dignity_during_arrestz<TestTier1Features.test_f3_04_vuong_ong_dignity_during_arrest¸   s�   ø€ å)Ñ+Ô+ˆ
Ø×%Ò%Ñ'Ô'ˆÝ" 5¨!Ñ,Ô,ð 	mð 	m‰IˆC�Ø Ð%Ð%­#Ð-xÐ-xÐ-xÐ-xÐIwÐ-xÑ-xÔ-xÑ*xÔ*xÐ%Ø—	’	Ðk #ÐkÐkÐeiÐkÐkÑlÔlÐløð	mð 	mr   c                 ó¨   — t          ¦   «         }t          t          j        d|t          j        ¦  «        ¦  «        }|                      |d¦  «         dS )uj   F3.5: Verify presence of codified feudal stoic expressions (Ä‘anh láº¡i, trÄ©u náº·ng, nÃ©n, hÃ m báº¡nh).uQ   (Ä‘anh láº¡i|trÄ©u náº·ng|nÃ©n thá»Ÿ|hÃ m báº¡nh|Ã¡nh nhÃ¬n xa xÄƒm|tráº§m máº·c)ug   Screenplay must substitute tears with feudal stoic micro-expressions (Ä‘anh láº¡i, trÄ©u náº·ng, nÃ©n).Nr7   )r(   r)   Úhas_stoic_expressionss      r   Ú.test_f3_05_feudal_stoicism_expression_presencez@TestTier1Features.test_f3_05_feudal_stoicism_expression_presenceÀ   sp   € å)Ñ+Ô+ˆ
Ý $¥R¤Yð  0Dð  FPõ  RTô  R_ñ  &`ô  &`ñ  !aô  !aÐØ�ŠØ!Øuñ	
ô 	
ð 	
ð 	
ð 	
r   c                 ól  — t           dz  }t           dz  }d}|                     ¦   «         r||                     d¬¦  «        z  }|                     ¦   «         r||                     d¬¦  «        z  }t          t	          j        d|t          j        ¦  «        ¦  «        }|                      |d¦  «         dS )	zHF4.1: Verify explicit Anti-Male-Tears section codified in Project Bible.úCHARACTER_BIBLE.mdúMASTER_BIBLE.mdrY   úutf-8©Úencodingu_   (Anti-Male-Tears|Nam nhÃ¢n tuyá»‡t Ä‘á»‘i khÃ´ng rÆ¡i lá»‡|quy chuáº©n diá»…n xuáº¥t nam nhÃ¢n)z3Project Bible must codify Anti-Male-Tears directiveN©r   ÚexistsÚ	read_textr8   r"   r9   r$   r    )r(   Úchar_bible_pathÚmaster_bible_pathÚcontentÚhas_directives        r   Ú-test_f4_01_anti_male_tears_directive_in_biblez?TestTier1Features.test_f4_01_anti_male_tears_directive_in_bibleÌ   sÖ   € å#Ð&:Ñ:ˆÝ%Ð(9Ñ9ÐØˆØ×!Ò!Ñ#Ô#ð 	CØ�×0Ò0¸'Ð0ÑBÔBÑBˆGØ×#Ò#Ñ%Ô%ð 	EØÐ(×2Ò2¸GÐ2ÑDÔDÑDˆGÝ�RœYð  (Jð  LSõ  UWô  Ubñ  cô  cñ  dô  dˆØ�Š˜Ð'\Ñ]Ô]Ð]Ð]Ð]r   c                 óø   — t           dz  }|                     ¦   «         r|                     d¬¦  «        nd}t          t	          j        d|t          j        ¦  «        ¦  «        }|                      |d¦  «         dS )zhF4.2: Verify Project Bible specifies male micro-expressions (jaw clench, heavy gaze, compressed breath).r§   r©   rª   rY   u[   (cÆ¡ máº·t Ä‘anh|hÃ m báº¡nh|Ã¡nh máº¯t trÄ©u náº·ng|hÆ¡i thá»Ÿ nÃ©n|jaw clench|heavy gaze)z6Project Bible must detail male stoic micro-expressionsNr¬   )r(   r¯   r±   Úhas_cuess       r   Ú0test_f4_02_male_stoic_micro_expressions_codifiedzBTestTier1Features.test_f4_02_male_stoic_micro_expressions_codifiedØ   s‘   € å#Ð&:Ñ:ˆØAP×AWÒAWÑAYÔAYÐa�/×+Ò+°WÐ+Ñ=Ô=Ð=Ð_aˆÝ�œ	ð  #Að  CJõ  LNô  LYñ  Zô  Zñ  [ô  [ˆØ�Š˜Ð"ZÑ[Ô[Ð[Ð[Ð[r   c                 óø   — t           dz  }|                     ¦   «         r|                     d¬¦  «        nd}t          t	          j        d|t          j        ¦  «        ¦  «        }|                      |d¦  «         dS )zWF4.3: Verify Project Bible codifies nuanced female acting standards avoiding melodrama.r§   r©   rª   rY   uB   (micro-expression|diá»…n xuáº¥t.*ná»¯|lÃ n thu thá»§y|Ä‘oan trang)z9Project Bible must codify nuanced female acting standardsNr¬   )r(   r¯   r±   Úhas_female_standardss       r   Ú*test_f4_03_female_nuanced_acting_standardsz<TestTier1Features.test_f4_03_female_nuanced_acting_standardsß   sŒ   € å#Ð&:Ñ:ˆØAP×AWÒAWÑAYÔAYÐa�/×+Ò+°WÐ+Ñ=Ô=Ð=Ð_aˆÝ#¥B¤IÐ.sÐu|õ  Aô  Lñ  %Mô  %Mñ   Nô   NÐØ�ŠÐ,Ð.iÑjÔjÐjÐjÐjr   c                 óø   — t           dz  }|                     ¦   «         r|                     d¬¦  «        nd}t          t	          j        d|t          j        ¦  «        ¦  «        }|                      |d¦  «         dS )zJF4.4: Verify Project Bible codifies authentic crowd / extras motion rules.r¨   r©   rª   rY   u,   (quáº§n chÃºng|extras|Ä‘Ã¡m Ä‘Ã´ng|du xuÃ¢n)z<Project Bible must specify crowd & extras behavior standardsNr¬   )r(   Ú
bible_pathr±   Ú	has_crowds       r   Ú(test_f4_04_crowd_and_extras_acting_rulesz:TestTier1Features.test_f4_04_crowd_and_extras_acting_rulesæ   sv   € åÐ!2Ñ2ˆ
Ø<F×<MÒ<MÑ<OÔ<OÐW�*×&Ò&°Ð&Ñ8Ô8Ð8ÐUWˆÝ�œÐ#RÐT[Õ]_Ô]jÑkÔkÑlÔlˆ	Ø�Š˜	Ð#aÑbÔbÐbÐbÐbr   c                 óø   — t           dz  }|                     ¦   «         r|                     d¬¦  «        nd}t          t	          j        d|t          j        ¦  «        ¦  «        }|                      |d¦  «         dS )ua   F4.5: Verify Project Bible mandates East Asian feudal decorum (Lá»… giÃ¡o, phong thÃ¡i nho nhÃ£).r¨   r©   rª   rY   u8   (phong kiáº¿n|nho gia|lá»… giÃ¡o|cá»• phong|Ä‘oan trang)zBProject Bible must mandate feudal decorum and historical composureNr¬   )r(   r»   r±   Úhas_decorums       r   Ú,test_f4_05_historical_feudal_decorum_mandatez>TestTier1Features.test_f4_05_historical_feudal_decorum_mandateí   sv   € åÐ!2Ñ2ˆ
Ø<F×<MÒ<MÑ<OÔ<OÐW�*×&Ò&°Ð&Ñ8Ô8Ð8ÐUWˆÝ�2œ9Ð%`ÐbiÕkmÔkxÑyÔyÑzÔzˆØ�Š˜Ð%iÑjÔjÐjÐjÐjr   c                 óÜ   — t          ¦   «         }|                      t          |¦  «        dk    d¦  «         d}t          j        |¦  «        }d|v sd|v rd}|                      |d¦  «         dS )	zGF5.1: Verify start frame prompt registry specifies 1280x720 resolution.r   z,gemini_banana_prompts.json must not be emptyFÚ1280x720Ú720pTzNGemini Banana prompt registry must enforce 720p (1280x720) sweet-spot standardN)r   r    r!   ÚjsonÚdumps)r(   Úbanana_dataÚ	res_foundÚraw_strs       r   Ú,test_f5_01_gemini_banana_resolution_standardz>TestTier1Features.test_f5_01_gemini_banana_resolution_standard÷   sx   € å.Ñ0Ô0ˆØ�Š�˜KÑ(Ô(¨1Ò,Ð.\Ñ]Ô]Ð]àˆ	Ý”*˜[Ñ)Ô)ˆØ˜Ð Ð  F¨gÐ$5Ð$5ØˆIØ�Š˜	Ð#sÑtÔtÐtÐtÐtr   c                 ó®   ‡— t          ¦   «         }t          j        |¦  «        Št          ˆfd„dD ¦   «         ¦  «        }|                      |d¦  «         dS )z^F5.2: Verify start frame prompts combine character anchor with specific environmental anchors.c              3   óD   •K  — | ]}|‰                      ¦   «         v V — Œd S r�   r   )r   ÚenvrÈ   s     €r   rf   zOTestTier1Features.test_f5_02_contextual_environment_blending.<locals>.<genexpr>  s0   øè è € ÐzÐz°�c˜WŸ]š]™_œ_Ð,ÐzÐzÐzÐzÐzÐzr   )u   suá»‘iu   cá»�u   má»™u
   báº¯c kinhu   vÆ°á»�nz
thanh minhzEStart frame prompts must combine character with environmental anchorsN)r   rÄ   rÅ   r—   r    )r(   rÆ   Úhas_envrÈ   s      @r   Ú*test_f5_02_contextual_environment_blendingz<TestTier1Features.test_f5_02_contextual_environment_blending  s[   ø€ å.Ñ0Ô0ˆÝ”*˜[Ñ)Ô)ˆÝÐzÐzÐzÐzÐ8yÐzÑzÔzÑzÔzˆØ�Š˜Ð!hÑiÔiÐiÐiÐir   c                 ó¨  ‡— t          ¦   «         }|                     di ¦  «        }g }|                     ¦   «         D ]n\  Š}t          ˆfd„dD ¦   «         ¦  «        rNt	          |t
          ¦  «        r|                     dd¦  «        nd}d|v rd|vrd|vr|                     ‰¦  «         Œo|                      t          |¦  «        d	d
|› �¦  «         dS )zXF5.3: Verify outdoor scene prompts do not directly feed raw studio portraits to Muse.ai.r   c              3   ó    •K  — | ]}|‰v V — Œ	d S r�   rd   )r   Úscr   s     €r   rf   zSTestTier1Features.test_f5_03_elimination_of_raw_studio_portraits.<locals>.<genexpr>  s'   øè è € ÐGÐG˜r�2˜�7ÐGÐGÐGÐGÐGÐGr   )Úscene04Úscene05Úscene06Úcharacter_asset_refrY   Ú
charactersÚbackgroundsÚ	keyframesr   z;Outdoor prompts directly referencing raw studio portraits: N)	r   r%   Úitemsr—   Ú
isinstanceÚdictrC   r‡   r!   )r(   r+   ÚpromptsÚoutdoor_raw_portraitsr…   Úrefr   s         @r   Ú.test_f5_03_elimination_of_raw_studio_portraitsz@TestTier1Features.test_f5_03_elimination_of_raw_studio_portraits	  s  ø€ å*Ñ,Ô,ˆ	Ø—-’-Ð 0°"Ñ5Ô5ˆà "ÐØ—M’M‘O”Oð 	4ð 	4‰DˆAˆqÝÐGÐGÐGÐGÐ%FÐGÑGÔGÑGÔGð 4Ý:DÀQÍÑ:MÔ:MÐU�a—e’eÐ1°2Ñ6Ô6Ð6ÐSU�Ø 3Ð&Ð&¨=ÀÐ+CÐ+CÈÐ[^ÐH^ÐH^Ø)×0Ò0°Ñ3Ô3Ð3øØ×ÒÝÐ%Ñ&Ô&¨ØaÐJ_ÐaÐañ	
ô 	
ð 	
ð 	
ð 	
r   c                 ó’  ‡— t          ¦   «         }|                     di ¦  «        }t          |                     ¦   «         ¦  «        dd…         }d}|D ]\}t	          |t
          ¦  «        r|                     dd¦  «        nt          |¦  «        Št          ˆfd„dD ¦   «         ¦  «        r|d	z  }Œ]|                      |d
d¦  «         dS )ui   F5.4: Verify motion prompts specify clear forward motion vectors (tiáº¿n tá»›i, bay vá»� phÃ­a trÆ°á»›c).r   Né   r   Úmotion_promptrY   c              3   óD   •K  — | ]}|‰                      ¦   «         v V — Œd S r�   r   )r   r�   rƒ   s     €r   rf   zOTestTier1Features.test_f5_04_directional_motion_vector_guard.<locals>.<genexpr>!  s0   øè è € ÐyÐy¨�1˜Ÿ
š
™œÐ$ÐyÐyÐyÐyÐyÐyr   )Úcamerau   chuyá»ƒn Ä‘á»™ngu   bÆ°á»›cÚbayu   tiáº¿nÚpanÚdollyr@   é   z=Motion prompts must specify active directional motion vectors)	r   r%   ÚlistÚvaluesrÚ   rÛ   Ústrr—   r'   )r(   r+   rÜ   Úsample_promptsÚprompts_with_motionÚprƒ   s         @r   Ú*test_f5_04_directional_motion_vector_guardz<TestTier1Features.test_f5_04_directional_motion_vector_guard  sÖ   ø€ å*Ñ,Ô,ˆ	Ø—-’-Ð 0°"Ñ5Ô5ˆÝ˜gŸnšnÑ.Ô.Ñ/Ô/°°°Ô4ˆØÐØð 	)ð 	)ˆAÝ1;¸A½tÑ1DÔ1DÐP�1—5’5˜¨"Ñ-Ô-Ð-Í#ÈaÉ&Ì&ˆDÝÐyÐyÐyÐyÐ.xÐyÑyÔyÑyÔyð )Ø# qÑ(Ð#øØ×ÒÐ 3°RÐ9xÑyÔyÐyÐyÐyr   c                 óÔ   — t          ¦   «         }t          j        |d¬¦  «        }t          t	          j        d|t          j        ¦  «        ¦  «        }|                      |d¦  «         dS )zbF5.5: Verify prompt instructions forbid reverse physics (no backward bird flight, no moonwalking).F©Úensure_asciiu\   (bay vá»� phÃ­a trÆ°á»›c|tuyá»‡t Ä‘á»‘i khÃ´ng|chuyá»ƒn Ä‘á»™ng tá»± nhiÃªn|thuáº­n chiá»�u)z8Motion prompts must guard against reverse optical motionN)r   rÄ   rÅ   r8   r"   r9   r$   r    )r(   r+   rÈ   Ú	has_guards       r   Ú&test_f5_05_reverse_physics_prohibitionz8TestTier1Features.test_f5_05_reverse_physics_prohibition%  sx   € å*Ñ,Ô,ˆ	Ý”*˜Y°UÐ;Ñ;Ô;ˆÝ�œð  $Cð  ELõ  NPô  N[ñ  \ô  \ñ  ]ô  ]ˆ	Ø�Š˜	Ð#]Ñ^Ô^Ð^Ð^Ð^r   c                 ó@  ‡— t          ¦   «         }|                     di ¦  «        }|                     ¦   «         D ]ã\  }}t          |t          ¦  «        r|                     dd¦  «        nd}t          |t          ¦  «        r|                     dd¦  «        nt          |¦  «        Šd|                     ¦   «         v s,d|                     ¦   «         v sd|                     ¦   «         v r5t          ˆfd„d	D ¦   «         ¦  «        }|                      |d
|› d�¦  «         ŒädS )zXF6.1: Verify off-screen / distant dialogue prompts contain mandatory closed-lips clause.r   Úaudio_promptrY   râ   u   tá»« xau   ngoÃ i khungúv.o.c              3   óD   •K  — | ]}|‰                      ¦   «         v V — Œd S r�   r   )r   r�   Úmotion_texts     €r   rf   z\TestTier1Features.test_f6_01_lip_sync_guard_present_in_offscreen_dialogue.<locals>.<genexpr>7  sL   øè è € ð  &Lð  &LÀ1 a¨;×+<Ò+<Ñ+>Ô+>Ð&>ð  &Lð  &Lð  &Lð  &Lð  &Lð  &Lr   )õ   khÃ©p cháº·tõ
   khÃ©p mÃ´iúlips closedõ   khÃ´ng máº¥p mÃ¡yzShot z6 has off-screen dialogue but missing closed-lips guardN)	r   r%   rÙ   rÚ   rÛ   rë   r   r—   r    )r(   r+   rÜ   Úshot_idÚ	shot_infoÚ
audio_textÚhas_closed_lipsrù   s          @r   Ú7test_f6_01_lip_sync_guard_present_in_offscreen_dialoguezITestTier1Features.test_f6_01_lip_sync_guard_present_in_offscreen_dialogue/  sO  ø€ å*Ñ,Ô,ˆ	Ø—-’-Ð 0°"Ñ5Ô5ˆØ")§-¢-¡/¤/ð 	zð 	zÑˆG�YÝ>HÈÕTXÑ>YÔ>YÐa˜Ÿš ~°rÑ:Ô:Ð:Ð_aˆJÝ@JÈ9ÕVZÑ@[Ô@[Ðo˜)Ÿ-š-¨¸Ñ<Ô<Ð<ÕadÐenÑaoÔaoˆKØ˜J×,Ò,Ñ.Ô.Ð.Ð.°.ÀJ×DTÒDTÑDVÔDVÐ2VÐ2VÐZ`Ðdn×dtÒdtÑdvÔdvÐZvÐZvÝ"%ð  &Lð  &Lð  &Lð  &Lð  IKð  &Lñ  &Lô  &Lñ  #Lô  #L�Ø—’ Ð1x¸Ð1xÐ1xÐ1xÑyÔyÐyøð	zð 	zr   c                 óX  ‡— t          ¦   «         }|                     di ¦  «        }|                     d¦  «        }|rmt          |t          ¦  «        r|                     dd¦  «        nt	          |¦  «        Št          ˆfd„dD ¦   «         ¦  «        }|                      |d¦  «         dS dS )	uo   F6.2: Verify Scene 08 Shot 02 (Kim Trá»�ng holding hairpin) enforces closed lips when Kiá»�u speaks off-screen.r   Úep01_scene08_shot02râ   rY   c              3   óD   •K  — | ]}|‰                      ¦   «         v V — Œd S r�   r   )r   r�   Úmotions     €r   rf   zOTestTier1Features.test_f6_02_scene08_kim_trong_hairpin_guard.<locals>.<genexpr>A  s0   øè è € Ð|Ð|°A˜A §¢¡¤Ð/Ð|Ð|Ð|Ð|Ð|Ð|r   ©rû   rú   rü   rý   uJ   Scene 08 Shot 02 must guard Kim Trá»�ng's lips when Kiá»�u calls from afarN)r   r%   rÚ   rÛ   rë   r—   r    )r(   r+   rÜ   Úc08_s02ró   r  s        @r   Ú*test_f6_02_scene08_kim_trong_hairpin_guardz<TestTier1Features.test_f6_02_scene08_kim_trong_hairpin_guard:  s¸   ø€ å*Ñ,Ô,ˆ	Ø—-’-Ð 0°"Ñ5Ô5ˆØ—+’+Ð3Ñ4Ô4ˆØð 	uÝ9CÀGÍTÑ9RÔ9RÐd�W—[’[ °"Ñ5Ô5Ð5ÕX[Ð\cÑXdÔXdˆFÝÐ|Ð|Ð|Ð|Ð9{Ð|Ñ|Ô|Ñ|Ô|ˆIØ�OŠO˜IÐ'sÑtÔtÐtÐtÐtð	uð 	ur   c                 óp  ‡— t          ¦   «         }|                     di ¦  «        }d„ |                     ¦   «         D ¦   «         }|D ]o}t          j        ||         ¦  «                             ¦   «         Šd‰v r=d‰v sd‰v r5t          ˆfd„dD ¦   «         ¦  «        }|                      |d|› d	�¦  «         Œpd
S )zUF6.3: Verify shots featuring on-screen characters with voiceover enforce closed lips.r   c                 ó¸   — g | ]W\  }}d t          j        |¦  «                             ¦   «         v s(dt          j        |¦  «                             ¦   «         v ¯U|‘ŒXS )r÷   u   ngÆ°á»�i dáº«n chuyá»‡n)rÄ   rÅ   r   ©r   r   r…   s      r   r   zLTestTier1Features.test_f6_03_voiceover_prompts_lip_guard.<locals>.<listcomp>H  s—   € ð  Lð  Lð  L™$˜!˜Q°V½t¼zÈ!¹}¼}×?RÒ?RÑ?TÔ?TÐ5TÐ5TÐXqÕuyÔuð  ABñ  vCô  vC÷  vIò  vIñ  vKô  vKð  YKð  YK�Að  YKð  YKð  YKr   u   nhÃ¢n váº­tu   khuÃ´n máº·tu   cáº­n cáº£nhc              3   ó    •K  — | ]}|‰v V — Œ	d S r�   rd   )r   r�   Úshot_strs     €r   rf   zKTestTier1Features.test_f6_03_voiceover_prompts_lip_guard.<locals>.<genexpr>L  s'   øè è € ÐzÐz°!  X ÐzÐzÐzÐzÐzÐzr   r  zVO shot z must have closed-lips guardN)r   r%   rÙ   rÄ   rÅ   r   r—   r    )r(   r+   rÜ   Úvo_shotsrþ   ró   r  s         @r   Ú&test_f6_03_voiceover_prompts_lip_guardz8TestTier1Features.test_f6_03_voiceover_prompts_lip_guardD  sí   ø€ å*Ñ,Ô,ˆ	Ø—-’-Ð 0°"Ñ5Ô5ˆð Lð  L '§-¢-¡/¤/ð  Lñ  Lô  LˆØð 	]ð 	]ˆGÝ”z '¨'Ô"2Ñ3Ô3×9Ò9Ñ;Ô;ˆHØ Ð(Ð(¨nÀÐ.HÐ.HÈNÐ^fÐLfÐLfÝÐzÐzÐzÐzÐ7yÐzÑzÔzÑzÔz�	Ø—’ 	Ð+[°gÐ+[Ð+[Ð+[Ñ\Ô\Ð\øð		]ð 	]r   c                 ó’   — t          ¦   «         }t          j        |d¬¦  «        }d|v pd|v pd|v }|                      |d¦  «         dS )z]F6.4: Verify closed-lips directive uses standardized terminology (Vietnamese and/or English).Frñ   rú   rü   rý   uK   Lip-sync guard must use standardized terminology (khÃ©p cháº·t/lips closed)N)r   rÄ   rÅ   r    )r(   r+   rÈ   Ú
has_phrases       r   Ú'test_f6_04_lip_sync_bilingual_directivez9TestTier1Features.test_f6_04_lip_sync_bilingual_directiveO  s\   € å*Ñ,Ô,ˆ	Ý”*˜Y°UÐ;Ñ;Ô;ˆØ# wÐ.Ðl°-À7Ð2JÐlÐNaÐelÐNlˆ
Ø�Š˜
Ð$qÑrÔrÐrÐrÐrr   c                 ó8  — t          ¦   «         }|                     di ¦  «        }d}|                     ¦   «         D ]G}t          j        |¦  «        }d|                     ¦   «         v sd|                     ¦   «         v r|dz  }ŒH|                      |dd¦  «         dS )	zJF6.5: Verify prompts classify dialogue context as on-screen vs off-screen.r   r   u   thoáº¡iÚaudior@   r1   z&Prompts must classify dialogue contextN)r   r%   rê   rÄ   rÅ   r   rS   )r(   r+   rÜ   Ú
classifiedrî   rƒ   s         r   Ú1test_f6_05_on_screen_vs_off_screen_classificationzCTestTier1Features.test_f6_05_on_screen_vs_off_screen_classificationV  sš   € å*Ñ,Ô,ˆ	Ø—-’-Ð 0°"Ñ5Ô5ˆØˆ
Ø—’Ñ!Ô!ð 	 ð 	 ˆAÝ”:˜a‘=”=ˆDØ˜DŸJšJ™LœLÐ(Ð(¨G°t·z²z±|´|Ð,CÐ,CØ˜a‘�
øØ×Ò˜: rÐ+SÑTÔTÐTÐTÐTr   c                 óÐ   — t          ¦   «         }|                     di ¦  «        }d„ |D ¦   «         }|                      t          |¦  «        ddt          |¦  «        › d�¦  «         dS )zZF7.1: Verify muse_ai_video_prompts.json defines >= 150 shots (target: 188 shots) for EP01.r   c                 ó>   — g | ]}d |                      ¦   «         v ¯|‘ŒS r   r   r   s     r   r   zUTestTier1Features.test_f7_01_muse_prompt_registry_ep01_shot_count.<locals>.<listcomp>h  ó*   € Ð@Ð@Ð@˜A¨F°a·g²g±i´iÐ,?Ð,?�aÐ,?Ð,?Ð,?r   r   zEP01 motion prompts count (z) must be >= 150 (target: 188).N)r   r%   r'   r!   )r(   r+   rÜ   r3   s       r   Ú/test_f7_01_muse_prompt_registry_ep01_shot_countzATestTier1Features.test_f7_01_muse_prompt_registry_ep01_shot_countd  su   € å*Ñ,Ô,ˆ	Ø—-’-Ð 0°"Ñ5Ô5ˆØ@Ð@ Ð@Ñ@Ô@ˆ
Ø×ÒÝ�
‰OŒO˜SØZ­#¨j©/¬/ÐZÐZÐZñ	
ô 	
ð 	
ð 	
ð 	
r   c                 óp   — t          ¦   «         }|                      t          |¦  «        dk    d¦  «         dS )zXF7.2: Verify gemini_banana_prompts.json contains corresponding EP01 start frame prompts.r   z6gemini_banana_prompts.json must contain prompt entriesN)r   r    r!   )r(   rÆ   s     r   Ú3test_f7_02_gemini_banana_prompt_registry_ep01_shotszETestTier1Features.test_f7_02_gemini_banana_prompt_registry_ep01_shotsn  s5   € å.Ñ0Ô0ˆØ�Š�˜KÑ(Ô(¨1Ò,Ð.fÑgÔgÐgÐgÐgr   c                 ó  ‡— t          ¦   «         }|                     di ¦  «        }d„ |D ¦   «         }t          j        dt          j        ¦  «        Šˆfd„|D ¦   «         }|                      t          |¦  «        dd|› �¦  «         dS )zKF7.3: Verify EP01 shot IDs adhere to canonical pattern ep01_sceneXX_shotYY.r   c                 ó>   — g | ]}d |                      ¦   «         v ¯|‘ŒS r   r   r   s     r   r   zJTestTier1Features.test_f7_03_shot_id_format_compliance.<locals>.<listcomp>w  r  r   z^ep01_scene\d{2}_shot\d{2}$c                 ó>   •— g | ]}‰                      |¦  «        °|‘ŒS rd   )Úmatch)r   r   Úpatterns     €r   r   zJTestTier1Features.test_f7_03_shot_id_format_compliance.<locals>.<listcomp>y  s*   ø€ ÐAÐAÐA˜°·²¸aÑ0@Ô0@ÐA�1ÐAÐAÐAr   r   zNon-conforming shot IDs: N)r   r%   r"   Úcompiler$   r‡   r!   )r(   r+   rÜ   r3   Úinvalidr"  s        @r   Ú$test_f7_03_shot_id_format_compliancez6TestTier1Features.test_f7_03_shot_id_format_compliances  s�   ø€ å*Ñ,Ô,ˆ	Ø—-’-Ð 0°"Ñ5Ô5ˆØ@Ð@ Ð@Ñ@Ô@ˆ
Ý”*Ð;½R¼]ÑKÔKˆØAÐAÐAÐA˜jÐAÑAÔAˆØ×Ò�˜W™œ qÐ*OÀgÐ*OÐ*OÑPÔPÐPÐPÐPr   c           	      óÈ  — t          ¦   «         }|                     di ¦  «        }d„ |                     ¦   «         D ¦   «         }g }|                     ¦   «         D ]J\  }}t          j        |d¬¦  «                             ¦   «         }d|vrd|vr|                     |¦  «         ŒK|                      t          |¦  «        dt          |¦  «        › d|d	d
…         › �¦  «         d	S )zNF7.4: Verify 100% of EP01 motion prompts include mandatory audio guard clause.r   c                 óF   — i | ]\  }}d |                      ¦   «         v ¯||“ŒS r   r   r  s      r   ú
<dictcomp>zRTestTier1Features.test_f7_04_audio_guard_clause_in_all_prompts.<locals>.<dictcomp>€  ó0   € ÐNÐNÐN™t˜q !¸&ÀAÇGÂGÁIÄIÐ:MÐ:M�a˜Ð:MÐ:MÐ:Mr   Frñ   u'   tuyá»‡t Ä‘á»‘i khÃ´ng sinh nháº¡c ná»�nzno musicr   z# shots missing audio guard clause:
Né   )	r   r%   rÙ   rÄ   rÅ   r   rC   r‡   r!   )r(   r+   rÜ   r3   Ú	unguardedr   r…   rƒ   s           r   Ú,test_f7_04_audio_guard_clause_in_all_promptsz>TestTier1Features.test_f7_04_audio_guard_clause_in_all_prompts|  s÷   € å*Ñ,Ô,ˆ	Ø—-’-Ð 0°"Ñ5Ô5ˆØNÐN w§}¢}¡¤ÐNÑNÔNˆ
Øˆ	Ø×$Ò$Ñ&Ô&ð 	$ð 	$‰DˆAˆqÝ”:˜a¨eÐ4Ñ4Ô4×:Ò:Ñ<Ô<ˆDØ8ÀÐDÐDÈÐ[_ÐI_ÐI_Ø× Ò  Ñ#Ô#Ð#øØ×ÒÝ�	‰NŒN˜AÝ�9‰~Œ~ÐRÐRÀ9ÈRÈaÈRÄ=ÐRÐRñ	
ô 	
ð 	
ð 	
ð 	
r   c                 ó  — t          ¦   «         }|                     di ¦  «        }d„ |                     ¦   «         D ¦   «         }g }|                     ¦   «         D ]ˆ\  }}t          |t          ¦  «        r|                     dd¦  «        nt          |¦  «        }t          |                     ¦   «         ¦  «        dk     r$|                     |t          |¦  «        f¦  «         Œ‰|  	                    t          |¦  «        dd|› �¦  «         dS )	zQF7.5: Verify EP01 prompts contain non-empty, detailed descriptions (>= 50 chars).r   c                 óF   — i | ]\  }}d |                      ¦   «         v ¯||“ŒS r   r   r  s      r   r(  zPTestTier1Features.test_f7_05_prompt_non_emptiness_and_length.<locals>.<dictcomp>�  r)  r   râ   rY   é2   r   zPrompts too short (<50 chars): N)
r   r%   rÙ   rÚ   rÛ   rë   r!   ÚstriprC   r‡   )r(   r+   rÜ   r3   Úshort_promptsr   r…   Úm_texts           r   Ú*test_f7_05_prompt_non_emptiness_and_lengthz<TestTier1Features.test_f7_05_prompt_non_emptiness_and_length‹  sù   € å*Ñ,Ô,ˆ	Ø—-’-Ð 0°"Ñ5Ô5ˆØNÐN w§}¢}¡¤ÐNÑNÔNˆ
ØˆØ×$Ò$Ñ&Ô&ð 	7ð 	7‰DˆAˆqÝ3=¸aÅÑ3FÔ3FÐR�Q—U’U˜?¨BÑ/Ô/Ð/ÍCÐPQÉFÌFˆFÝ�6—<’<‘>”>Ñ"Ô" RÒ'Ð'Ø×$Ò$ a­¨V©¬Ð%5Ñ6Ô6Ð6øØ×Ò�˜]Ñ+Ô+¨QÐ0aÐR_Ð0aÐ0aÑbÔbÐbÐbÐbr   c                 ó¾   — ddl m}  |¦   «         }|                      t          |d¦  «        d¦  «         |                      t	          |j        ¦  «        d¦  «         dS )zJF8.1: Verify AudioContinuityEngine implements stitch_with_audio_crossfade.r   ©ÚAudioContinuityEngineÚstitch_with_audio_crossfadez*Missing stitch_with_audio_crossfade methodz,stitch_with_audio_crossfade must be callableN)Úaudio_continuity_enginer6  r    ÚhasattrÚcallabler7  ©r(   r6  Úengines      r   Ú(test_f8_01_crossfade_method_availabilityz:TestTier1Features.test_f8_01_crossfade_method_availabilityš  sh   € àAÐAÐAÐAÐAÐAØ&Ð&Ñ(Ô(ˆØ�Š� Ð(EÑFÔFÐHtÑuÔuÐuØ�Š� Ô!CÑDÔDÐFtÑuÔuÐuÐuÐur   c                 óŠ   — ddl }ddlm} |                     |j        ¦  «        }d|v pd|v }|                      |d¦  «         dS )z_F8.2: Verify equal-power crossfade curve (qsin or cbrt) is used in stitch_with_audio_crossfade.r   Nr5  ÚqsinÚcbrtz6Audio crossfade must use equal-power curve (qsin/cbrt))Úinspectr8  r6  Ú	getsourcer7  r    )r(   rA  r6  ÚsrcÚ	has_curves        r   Ú*test_f8_02_equal_power_curve_configurationz<TestTier1Features.test_f8_02_equal_power_curve_configuration¡  sa   € àˆˆˆØAÐAÐAÐAÐAÐAØ×ÒÐ 5Ô QÑRÔRˆØ˜c�MÐ2 V¨s ]ˆ	Ø�Š˜	Ð#[Ñ\Ô\Ð\Ð\Ð\r   c                 óæ   — ddl }ddlm} |                     |j        ¦  «        }|j                             d¦  «        }|                      |d¦  «         |                      |j	        dd¦  «         dS )z6F8.3: Verify default crossfade duration is 1.0 second.r   Nr5  Úcrossfade_durz5stitch_with_audio_crossfade must accept crossfade_durg      ð?z'Default crossfade duration must be 1.0s)
rA  r8  r6  Ú	signaturer7  Ú
parametersr%   rR   r‡   Údefault)r(   rA  r6  ÚsigÚparams        r   Ú%test_f8_03_default_crossfade_durationz7TestTier1Features.test_f8_03_default_crossfade_duration©  s�   € àˆˆˆØAÐAÐAÐAÐAÐAØ×ÒÐ 5Ô QÑRÔRˆØ”×"Ò" ?Ñ3Ô3ˆØ×Ò˜UÐ$[Ñ\Ô\Ð\Ø×Ò˜œ¨Ð-VÑWÔWÐWÐWÐWr   c                 óX   — ddl m} |                      t          |¦  «        d¦  «         dS )zYF8.4: Verify production_orchestrator integrates AudioContinuityEngine or seamless concat.r   )Úconcat_scene_shotsz#concat_scene_shots must be callableN)Úproduction_orchestratorrO  r    r:  )r(   rO  s     r   Ú4test_f8_04_production_orchestrator_crossfade_defaultzFTestTier1Features.test_f8_04_production_orchestrator_crossfade_default²  s8   € à>Ð>Ð>Ð>Ð>Ð>Ø�Š�Ð!3Ñ4Ô4Ð6[Ñ\Ô\Ð\Ð\Ð\r   c                 ó|   — ddl }ddlm} |                     |j        ¦  «        }|                      d|d¦  «         dS )z=F8.5: Verify inspect_sequence warns on volume delta > 3.0 dB.r   Nr5  z3.0z7inspect_sequence must threshold volume deltas at 3.0 dB)rA  r8  r6  rB  Úinspect_sequencerH   ©r(   rA  r6  rC  s       r   Ú)test_f8_05_delta_volume_warning_thresholdz;TestTier1Features.test_f8_05_delta_volume_warning_threshold·  sQ   € àˆˆˆØAÐAÐAÐAÐAÐAØ×ÒÐ 5Ô FÑGÔGˆØ�Š�e˜SÐ"[Ñ\Ô\Ð\Ð\Ð\r   c                 ó&  — t           dz  }t           dz  }t          |                     d¦  «        ¦  «        t          |                     d¦  «        ¦  «        z   t          |                     d¦  «        ¦  «        z   t          |                     d¦  «        ¦  «        z   }t          dz  }|                     ¦   «         o)d|                     d¬¦  «                             ¦   «         v }|                      t          |¦  «        d	k    p|d
¦  «         dS )zLF9.1: Verify festival crowd ambience audio asset exists in 04_Assets/audio/.r  Ú	audio_sfxz
*festival*z
*ambience*úCINEMATIC_AUDIO_PIPELINE.mdzstem 2r©   rª   r   z3Festival ambience asset or specification must existN)	r   ré   Úglobr   r­   r®   r   r    r!   )r(   Ú	audio_dirÚsfx_dirÚ
candidatesr»   Úhas_specs         r   Ú+test_f9_01_festival_ambience_asset_presencez=TestTier1Features.test_f9_01_festival_ambience_asset_presenceÁ  sò   € å Ñ(ˆ	Ý˜{Ñ*ˆÝ˜)Ÿ.š.¨Ñ6Ô6Ñ7Ô7½$¸y¿~º~ÈlÑ?[Ô?[Ñ:\Ô:\Ñ\Ý˜'Ÿ,š, |Ñ4Ô4Ñ5Ô5ñ6Ý8<¸W¿\º\È,Ñ=WÔ=WÑ8XÔ8XñYˆ
õ Ð!>Ñ>ˆ
Ø×$Ò$Ñ&Ô&Ðe¨8°z×7KÒ7KÐU\Ð7KÑ7]Ô7]×7cÒ7cÑ7eÔ7eÐ+eˆØ�Š�˜J™œ¨!Ò+Ð7¨xÐ9nÑoÔoÐoÐoÐor   c                 óâ   — t           dz  }|                     ¦   «         r|                     d¬¦  «        nd}t          t	          j        d|¦  «        ¦  «        }|                      |d¦  «         dS )z]F9.2: Verify festival ambience specification or file covers festival scene sequence (>= 60s).rX  r©   rª   rY   u   (50s|60s|180s|liÃªn tá»¥c)z?Ambience bed track must cover multi-shot sequences continuouslyN)r   r­   r®   r8   r"   r9   r    )r(   r»   r±   Úhas_coverages       r   Ú+test_f9_02_ambience_audio_duration_coveragez=TestTier1Features.test_f9_02_ambience_audio_duration_coverageÌ  so   € åÐ!>Ñ>ˆ
Ø<F×<MÒ<MÑ<OÔ<OÐW�*×&Ò&°Ð&Ñ8Ô8Ð8ÐUWˆÝ�BœIÐ&CÀWÑMÔMÑNÔNˆØ�Š˜Ð&gÑhÔhÐhÐhÐhr   c                 ór   — ddl m} ddl}|                     |¦  «        }|                      d|d¦  «         dS )zDF9.3: Verify audio pipeline enforces 48000 Hz sample rate for stems.r   r5  NÚ48000z.Audio engine must enforce 48000 Hz sample rate)r8  r6  rA  rB  rH   )r(   r6  rA  rC  s       r   Ú*test_f9_03_ambience_format_and_sample_ratez<TestTier1Features.test_f9_03_ambience_format_and_sample_rateÓ  sN   € àAÐAÐAÐAÐAÐAØˆˆˆØ×ÒÐ 5Ñ6Ô6ˆØ�Š�g˜sÐ$TÑUÔUÐUÐUÐUr   c                 óF  — t           dz  }|                      |                     ¦   «         d¦  «         |                     d¬¦  «        }|                      d|¦  «         |                      d|¦  «         |                      d|¦  «         |                      d|¦  «         d	S )
zEF9.4: Verify CINEMATIC_AUDIO_PIPELINE.md defines 4-stem architecture.rX  z&CINEMATIC_AUDIO_PIPELINE.md must existr©   rª   zStem 1zStem 2zStem 3zStem 4N)r   r    r­   r®   rH   )r(   r»   r±   s      r   Ú,test_f9_04_four_stem_architecture_compliancez>TestTier1Features.test_f9_04_four_stem_architecture_complianceÚ  sš   € åÐ!>Ñ>ˆ
Ø�Š˜
×)Ò)Ñ+Ô+Ð-UÑVÔVÐVØ×&Ò&°Ð&Ñ8Ô8ˆØ�Š�h Ñ(Ô(Ð(Ø�Š�h Ñ(Ô(Ð(Ø�Š�h Ñ(Ô(Ð(Ø�Š�h Ñ(Ô(Ð(Ð(Ð(r   c                 ón   — ddl m}  |¦   «         }|                      t          |d¦  «        d¦  «         dS )zIF9.5: Verify AudioContinuityEngine supports dynamic ducking and layering.r   r5  Úlayer_bgm_with_duckingz%Must implement layer_bgm_with_duckingN)r8  r6  r    r9  r;  s      r   Ú(test_f9_05_ambience_layering_integrationz:TestTier1Features.test_f9_05_ambience_layering_integrationä  sH   € àAÐAÐAÐAÐAÐAØ&Ð&Ñ(Ô(ˆØ�Š� Ð(@ÑAÔAÐCjÑkÔkÐkÐkÐkr   c                 óš   — ddl }ddlm} |                     |j        ¦  «        }|                      |j        d         j        d¦  «         dS )z4F10.1: Verify normalize_loudness targets -14.0 LUFS.r   Nr5  Útarget_lufsç      ,À©rA  r8  r6  rH  Únormalize_loudnessr‡   rI  rJ  ©r(   rA  r6  rK  s       r   Ú%test_f10_01_target_lufs_configurationz7TestTier1Features.test_f10_01_target_lufs_configurationí  sZ   € àˆˆˆØAÐAÐAÐAÐAÐAØ×ÒÐ 5Ô HÑIÔIˆØ×Ò˜œ¨Ô6Ô>ÀÑFÔFÐFÐFÐFr   c                 óš   — ddl }ddlm} |                     |j        ¦  «        }|                      |j        d         j        d¦  «         dS )z@F10.2: Verify normalize_loudness targets True Peak <= -1.0 dBTP.r   Nr5  Ú	target_tpg      ð¿rm  ro  s       r   Ú*test_f10_02_target_true_peak_configurationz<TestTier1Features.test_f10_02_target_true_peak_configurationô  sZ   € àˆˆˆØAÐAÐAÐAÐAÐAØ×ÒÐ 5Ô HÑIÔIˆØ×Ò˜œ¨Ô4Ô<¸dÑCÔCÐCÐCÐCr   c                 óž   — ddl }ddlm} |                     |j        ¦  «        }|                      |j        d         j        dd¬¦  «         dS )zEF10.3: Verify normalize_loudness targets LRA in range 9.0 to 11.0 LU.r   Nr5  Ú
target_lrag      "@g       @)Údelta)rA  r8  r6  rH  rn  ÚassertAlmostEqualrI  rJ  ro  s       r   Ú/test_f10_03_target_loudness_range_configurationzATestTier1Features.test_f10_03_target_loudness_range_configurationû  s`   € àˆˆˆØAÐAÐAÐAÐAÐAØ×ÒÐ 5Ô HÑIÔIˆØ×Ò˜sœ~¨lÔ;ÔCÀSÐPSÐÑTÔTÐTÐTÐTr   c                 ó¦   — ddl }ddlm} |                     |j        ¦  «        }|                      d|¦  «         |                      d|¦  «         dS )zCF10.4: Verify FFmpeg loudnorm filter string sets I=-14 and TP=-1.0.r   Nr5  z	loudnorm=rk  )rA  r8  r6  rB  rn  rH   rT  s       r   Ú/test_f10_04_loudnorm_filter_string_constructionzATestTier1Features.test_f10_04_loudnorm_filter_string_construction  sb   € àˆˆˆØAÐAÐAÐAÐAÐAØ×ÒÐ 5Ô HÑIÔIˆØ�Š�k 3Ñ'Ô'Ð'Ø�Š�m SÑ)Ô)Ð)Ð)Ð)r   c                 óz   — d}d}||z
  }||z   }|                       |d¦  «         |                       |d¦  «         dS )zLF10.5: Verify YouTube Green Dollar compliance window is [-14.5, -13.5] LUFS.rl  g      à?g      -Àg      +ÀN)r‡   )r(   ÚtargetÚ	toleranceÚmin_lufsÚmax_lufss        r   Ú(test_f10_05_measurement_tolerance_windowz:TestTier1Features.test_f10_05_measurement_tolerance_window
  sT   € ð ˆØˆ	Ø˜IÑ%ˆØ˜IÑ%ˆØ×Ò˜ 5Ñ)Ô)Ð)Ø×Ò˜ 5Ñ)Ô)Ð)Ð)Ð)r   c                 ór   — ddl m} |                      |¦  «         |                      |j        d¦  «         dS )zEF11.1: Verify web_review.server defines a valid FastAPI app instance.r   ©Úappu(   Tháº­p NgÅ© NiÃªn - Cinema Review StudioN)Úserverrƒ  rR   r‡   Útitle)r(   rƒ  s     r   Ú&test_f11_01_fastapi_app_instance_validz8TestTier1Features.test_f11_01_fastapi_app_instance_valid  sG   € àÐÐÐÐÐØ×Ò˜SÑ!Ô!Ð!Ø×Ò˜œÐ$NÑOÔOÐOÐOÐOr   c                 ó@  — ddl m} ddlm}  ||¦  «        }|                     d¦  «        }|                      |j        d¦  «         |                     ¦   «         }|                      d|¦  «         |                      d|¦  «         |                      d|¦  «         d	S )
zAF11.2: Verify /api/library returns expected dictionary structure.r   r‚  ©Ú
TestClientz/api/libraryéÈ   ÚsummaryÚfeature_mastersÚ
raw_scenesN©	r„  rƒ  Ústarlette.testclientr‰  r%   r‡   Ústatus_coderÄ   rH   ©r(   rƒ  r‰  ÚclientÚrespÚdatas         r   Ú&test_f11_02_library_endpoint_structurez8TestTier1Features.test_f11_02_library_endpoint_structure  s­   € àÐÐÐÐÐØ3Ð3Ð3Ð3Ð3Ð3Ø�˜C‘”ˆØ�zŠz˜.Ñ)Ô)ˆØ×Ò˜Ô)¨3Ñ/Ô/Ð/Ø�yŠy‰{Œ{ˆØ�Š�i Ñ&Ô&Ð&Ø�ŠÐ'¨Ñ.Ô.Ð.Ø�Š�l DÑ)Ô)Ð)Ð)Ð)r   c                 ó¨   — ddl m} ddlm}  ||¦  «        }|                     d¦  «        }|                      |j        dd|j        › d�¦  «         dS )	z?F11.3: Verify /api/shots endpoint is registered and responsive.r   r‚  rˆ  z
/api/shotsrŠ  z$/api/shots endpoint returned status ú (expected 200)N©r„  rƒ  r�  r‰  r%   r‡   r�  ©r(   rƒ  r‰  r’  r“  s        r   Ú%test_f11_03_shots_endpoint_registeredz7TestTier1Features.test_f11_03_shots_endpoint_registered)  s   € àÐÐÐÐÐØ3Ð3Ð3Ð3Ð3Ð3Ø�˜C‘”ˆØ�zŠz˜,Ñ'Ô'ˆà×ÒØÔ˜cØT°4Ô3CÐTÐTÐTñ	
ô 	
ð 	
ð 	
ð 	
r   c                 ó¨   — ddl m} ddlm}  ||¦  «        }|                     d¦  «        }|                      |j        dd|j        › d�¦  «         dS )	z@F11.4: Verify /api/scenes endpoint is registered and responsive.r   r‚  rˆ  z/api/scenesrŠ  z%/api/scenes endpoint returned status r—  Nr˜  r™  s        r   Ú&test_f11_04_scenes_endpoint_registeredz8TestTier1Features.test_f11_04_scenes_endpoint_registered5  s   € àÐÐÐÐÐØ3Ð3Ð3Ð3Ð3Ð3Ø�˜C‘”ˆØ�zŠz˜-Ñ(Ô(ˆØ×ÒØÔ˜cØU°DÔ4DÐUÐUÐUñ	
ô 	
ð 	
ð 	
ð 	
r   c                 ó  — ddl m} ddlm}  ||¦  «        }|                     d¦  «        }|                      |j        d¦  «         |                     ¦   «         }|                      d|¦  «         |                      d|¦  «         dS )	z?F11.5: Verify /api/status endpoint returns gen_state structure.r   r‚  rˆ  z/api/statusrŠ  ÚstatusÚlogsNrŽ  r‘  s         r   Ú$test_f11_05_status_endpoint_contractz6TestTier1Features.test_f11_05_status_endpoint_contract@  s˜   € àÐÐÐÐÐØ3Ð3Ð3Ð3Ð3Ð3Ø�˜C‘”ˆØ�zŠz˜-Ñ(Ô(ˆØ×Ò˜Ô)¨3Ñ/Ô/Ð/Ø�yŠy‰{Œ{ˆØ�Š�h Ñ%Ô%Ð%Ø�Š�f˜dÑ#Ô#Ð#Ð#Ð#r   c                 óª  — ddl }|                     t          j        t	          t
          dz  ¦  «        dgdddd¬¦  «        }|                      |j        d¦  «         |j        pd	}|  	                    d
|¦  «         |  	                    d|¦  «         |  	                    d|¦  «         |  	                    d|¦  «         |  	                    d|¦  «         dS )zHF12.1: Verify production_orchestrator.py defines required CLI arguments.r   Nzproduction_orchestrator.pyz--helpTr©   Úreplace)Úcapture_outputrƒ   r«   ÚerrorsrY   z--statusz--list-shotsz--render-shotz--concat-scenez--master-audio)
Ú
subprocessÚrunÚsysÚ
executablerë   r   r‡   Ú
returncodeÚstdoutrH   )r(   r¥  ÚprocÚ	help_texts       r   Ú-test_f12_01_production_orchestrator_cli_flagsz?TestTier1Features.test_f12_01_production_orchestrator_cli_flagsN  sÛ   € àÐÐÐØ�~Š~ÝŒ^�S¥Ð0LÑ!LÑMÔMÈxÐXØ d°WÀYð ñ 
ô 
ˆð 	×Ò˜œ¨!Ñ,Ô,Ð,Ø”KÐ% 2ˆ	Ø�Š�j )Ñ,Ô,Ð,Ø�Š�n iÑ0Ô0Ð0Ø�Š�o yÑ1Ô1Ð1Ø�ŠÐ&¨	Ñ2Ô2Ð2Ø�ŠÐ&¨	Ñ2Ô2Ð2Ð2Ð2r   c                 ót   — ddl }|                      t          |d¦  «        pt          |d¦  «        ¦  «         dS )z=F12.2: Verify run_shot.py exports run_shot_pipeline function.r   NÚrun_shot_pipelineÚmain)Úrun_shotr    r9  )r(   r±  s     r   Ú&test_f12_02_run_shot_pipeline_callablez8TestTier1Features.test_f12_02_run_shot_pipeline_callable]  s>   € àˆˆˆØ�Š� Ð*=Ñ>Ô>Ð[Å'È(ÐTZÑB[ÔB[Ñ\Ô\Ð\Ð\Ð\r   c                 ó‚   — ddl m}  |di ¦  «        }|                      |du pt          |t          ¦  «        ¦  «         dS )zHF12.3: Verify resolve_start_frame looks up tail frame of preceding shot.r   )Úresolve_start_frameÚep01_scene02_shot02N)rP  r´  r    rÚ   rë   )r(   r´  Úresolveds      r   Ú!test_f12_03_shot_resolution_logicz3TestTier1Features.test_f12_03_shot_resolution_logicb  sS   € à?Ð?Ð?Ð?Ð?Ð?à&Ð&Ð'<¸bÑAÔAˆà�Š˜ DÐ(ÐE­J°xÅÑ,EÔ,EÑFÔFÐFÐFÐFr   c                 ó¨   — ddl m}  |d¦  «        }|r>d„ |D ¦   «         }d„ |D ¦   «         }|                      |t          |¦  «        d¦  «         dS dS )z;F12.4: Verify get_shots_for_scene orders shots numerically.r   )Úget_shots_for_sceneÚep01_scene01c                 ó   — g | ]
}|d          ‘ŒS )r   rd   ©r   Úss     r   r   zNTestTier1Features.test_f12_04_get_shots_for_scene_ordering.<locals>.<listcomp>o  s   € Ð,Ð,Ð, ˜˜!œÐ,Ð,Ð,r   c                 óx   — g | ]7}t          t          j        d |¦  «                             d¦  «        ¦  «        ‘Œ8S )z	shot(\d+)r@   )rD   r"   r9   rE   r¼  s     r   r   zNTestTier1Features.test_f12_04_get_shots_for_scene_ordering.<locals>.<listcomp>p  s:   € ÐOÐOÐOÀ•C�œ	 ,°Ñ2Ô2×8Ò8¸Ñ;Ô;Ñ<Ô<ÐOÐOÐOr   z/Shots must be sorted numerically by shot numberN)rP  r¹  r‡   Úsorted)r(   r¹  ÚshotsÚshot_idsÚnumss        r   Ú(test_f12_04_get_shots_for_scene_orderingz:TestTier1Features.test_f12_04_get_shots_for_scene_orderingj  s„   € à?Ð?Ð?Ð?Ð?Ð?Ø#Ð# NÑ3Ô3ˆØð 	dØ,Ð, eÐ,Ñ,Ô,ˆHØOÐOÀhÐOÑOÔOˆDØ×Ò˜T¥6¨$¡<¤<Ð1bÑcÔcÐcÐcÐcð	dð 	dr   c                 óR   — ddl m}  |d¦  «        }|                      |¦  «         dS )zEF12.5: Verify find_tail_frame discovers tail frames in KEYFRAMES_DIR.r   )Úfind_tail_frameÚnonexistent_shotN)rP  rÅ  ÚassertIsNone)r(   rÅ  Útfs      r   Ú test_f12_05_tail_frame_discoveryz2TestTier1Features.test_f12_05_tail_frame_discoverys  s>   € à;Ð;Ð;Ð;Ð;Ð;àˆ_Ð/Ñ0Ô0ˆØ×Ò˜"ÑÔÐÐÐr   c                 óL  — t           dz  }|                      |                     ¦   «         d¦  «         t          |dd¬¦  «        5 }|                     ¦   «         }ddd¦  «         n# 1 swxY w Y   t          |t          |¦  «        d¦  «        }|                      |¦  «         dS )zIF13.1: Verify assemble_ep01_feature.py exists and is syntactically valid.úassemble_ep01_feature.pyz#assemble_ep01_feature.py must existÚrr©   rª   NÚexec)r   r    r­   ÚopenÚreadr#  rë   rR   )r(   ÚpathÚfÚcodeÚcompileds        r   Ú,test_f13_01_assemble_feature_script_presencez>TestTier1Features.test_f13_01_assemble_feature_script_presence}  sÏ   € åÐ8Ñ8ˆØ�Š˜Ÿš™œÐ'LÑMÔMÐMå�$˜ gÐ.Ñ.Ô.ð 	°!Ø—6’6‘8”8ˆDð	ð 	ð 	ñ 	ô 	ð 	ð 	ð 	ð 	ð 	ð 	øøøð 	ð 	ð 	ð 	å˜4¥ T¡¤¨FÑ3Ô3ˆØ×Ò˜XÑ&Ô&Ð&Ð&Ð&s   ÁA&Á&A*Á-A*c                 óT   — |                       t          j        ¦   «         d¦  «         dS )zCF13.2: Verify 06_Exports directory exists and holds master outputs.z06_Exports must existN)r    r	   r­   )r(   s    r   Ú)test_f13_02_export_directory_master_filesz;TestTier1Features.test_f13_02_export_directory_master_files‡  s&   € à�Š�Ô*Ñ,Ô,Ð.EÑFÔFÐFÐFÐFr   c                 óž   — t           dz  }|                     d¬¦  «        }|                      d|¦  «         |                      d|¦  «         dS )zHF13.3: Verify assembly scripts map both video [v] and audio [a] streams.rË  r©   rª   z-map", "[v]"z-map", "[a]"N)r   r®   rH   ©r(   rÐ  r±   s      r   Ú.test_f13_03_ffmpeg_stream_mapping_verificationz@TestTier1Features.test_f13_03_ffmpeg_stream_mapping_verification‹  sN   € åÐ8Ñ8ˆØ—.’.¨'�.Ñ2Ô2ˆØ�Š�n gÑ.Ô.Ð.Ø�Š�n gÑ.Ô.Ð.Ð.Ð.r   c                 óž   — t          j        d¦  «        D ]7}|                     d¬¦  «        }|                      d||j        › d�¦  «         Œ8dS )zSF13.4: Verify assembly scripts DO NOT use OpenCV cv2.VideoWriter for master concat.zassemble_*.pyr©   rª   zcv2.VideoWriterz; must not use cv2.VideoWriter (violates audio preservation)N)r   rY  r®   ÚassertNotInÚname)r(   Úscriptr±   s      r   Ú&test_f13_04_no_opencv_concat_violationz8TestTier1Features.test_f13_04_no_opencv_concat_violation’  sl   € å"Ô'¨Ñ8Ô8ð 	ð 	ˆFØ×&Ò&°Ð&Ñ8Ô8ˆGØ×ÒØ! 7Ø”;Ð[Ð[Ð[ñô ð ð ð	ð 	r   c                 óÂ   — t           dz  }|                     d¬¦  «        }|                      d|                     ¦   «         ¦  «         |                      d|¦  «         dS )zAF13.5: Verify assembly scripts encode audio with AAC at 48000 Hz.rË  r©   rª   Úaacrc  N)r   r®   rH   r   rØ  s      r   Ú(test_f13_05_master_audio_aac_sample_ratez:TestTier1Features.test_f13_05_master_audio_aac_sample_rate›  sV   € åÐ8Ñ8ˆØ—.’.¨'�.Ñ2Ô2ˆØ�Š�e˜WŸ]š]™_œ_Ñ-Ô-Ð-Ø�Š�g˜wÑ'Ô'Ð'Ð'Ð'r   N)EÚ__name__Ú
__module__Ú__qualname__Ú__doc__r.   r5   r=   rO   rW   r`   rl   rq   ru   r}   rŠ   rœ   rŸ   r¢   r¥   r³   r¶   r¹   r½   rÀ   rÉ   rÎ   rß   rï   rô   r  r	  r  r  r  r  r  r%  r,  r3  r=  rE  rM  rQ  rU  r^  ra  rd  rf  ri  rp  rs  rx  rz  r€  r†  r•  rš  rœ  r   r­  r²  r·  rÃ  rÉ  rÔ  rÖ  rÙ  rÞ  rá  rd   r   r   r   r      st  € € € € € ðð ð
ð 
ð 
ð
ð 
ð 
ðSð Sð Sðgð gð gðbð bð bð"rð rð rðhð hð hðnð nð nðxð xð xðfð fð fð"
ð 
ð 
ð]ð ]ð ]ð^ð ^ð ^ðmð mð mð
ð 
ð 
ð
^ð 
^ð 
^ð\ð \ð \ðkð kð kðcð cð cðkð kð kð	uð 	uð 	uðjð jð jð
ð 
ð 
ð 
zð 
zð 
zð_ð _ð _ð	zð 	zð 	zðuð uð uð	]ð 	]ð 	]ðsð sð sð	Uð 	Uð 	Uð
ð 
ð 
ðhð hð hð
Qð Qð Qð
ð 
ð 
ð
cð 
cð 
cðvð vð vð]ð ]ð ]ðXð Xð Xð]ð ]ð ]ð
]ð ]ð ]ð	pð 	pð 	pðið ið iðVð Vð Vð)ð )ð )ðlð lð lðGð Gð GðDð Dð DðUð Uð Uð*ð *ð *ð*ð *ð *ðPð Pð Pð
*ð 
*ð 
*ð

ð 

ð 

ð	
ð 	
ð 	
ð	$ð 	$ð 	$ð3ð 3ð 3ð]ð ]ð ]ð
Gð Gð Gðdð dð dðð ð ð'ð 'ð 'ðGð Gð Gð/ð /ð /ðð ð ð(ð (ð (ð (ð (r   r   Ú__main__)'rå  ÚbuiltinsÚ@py_builtinsÚ_pytest.assertion.rewriteÚ	assertionÚrewriteÚ
@pytest_arÚosr§  r"   rÄ   ÚunittestÚpathlibr   Ú__file__ÚresolveÚparentÚ	TESTS_DIRÚBASE_DIRrë   rÐ  ÚinsertÚtestsr   r   r   r   r   r	   r
   r   r   r   r   r   ÚTestCaser   râ  r°  rd   r   r   ú<module>rø     sÞ  ððð ð
 € € € € € € € € € € € € €	€	€	€	Ø 
€
€
€
Ø 	€	€	€	Ø €€€Ø €€€Ø Ð Ð Ð Ð Ð ð ˆD�‰NŒN×"Ò"Ñ$Ô$Ô+€	ØÔ€Ø€3€x�=„=˜œÐ Ð Ø„H‡O‚O�A�s�s˜8‘}”}Ñ%Ô%Ð%Ø€3€y�>„>˜œÐ!Ð!Ø„H‡O‚O�A�s�s˜9‘~”~Ñ&Ô&Ð&ðð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ðD
(ð D
(ð D
(ð D
(ð D
(˜Ô)ñ D
(ô D
(ð D
(ðN ˆzÒÐØ€H„M�O„O€O€O€Oð Ðr   